Предположим, у меня тысячи пользователей, эти пользователи используются в отношениях «многие ко многим» с «компаниями».
Теперь я хочу удалить всех «неактивных» пользователей (а их большинство) из списка компаний.
Есть трюк, который я мог бы использовать с remove
:
for user in User.filter(active=False):
Company.customers.remove(user)
Однако это также приведет к тысячам обращений к базе данных, что сделает процесс очень медленным. Есть ли способ сделать это в Джанго? Не заходя в rawsql?
Эта команда выполняется очень редко, однако она разрывает соединение django/azure с базами данных клиентов. Из-за конфиденциальности я не могу предоставить (и не знаю как разработчик) точные детали, только сервер регистрирует, что это занимает более 10 минут. сделать это для каждой компании для каждого пользователя.
Почему «Без перехода в rawsql»?
that it takes 10+ minutes to do this for every company for every user
Удаление одного пользователя занимает более 10 минут? Я бы сказал, что 10 секунд — это очень долго, но 10 минут — это просто потрясающе. Похоже, у вас проблемы посерьёзнее, чем просто это удаление. Просто удаление показывает вам проблему. Проверьте свои запросы, проверьте планы запросов и улучшите свои стратегии индексирования. Похоже, есть что выиграть.
Всю эту «опу-буффу» можно свести к вот этому просто, быстро и понятно delete from users where not active and company_id = ? cascade
.
@FrankHeikens, конечно, не один пользователь, он удаляет тысячи полей пользователей-компаний. И я особенно заметил, что метод получения для пользователей получает много дополнительных данных.
@Stefanov.sm: Stefanov.sm: Я думаю, что следует удалять не самих неактивных пользователей, а отношения m2m, которые их связывают.
@willeM_VanOnsem Хорошо, процитируйте ОП: «Я хочу удалить всех «неактивных» пользователей (а это большинство) из списка компаний» (выделено жирным шрифтом). Однако удаление только отношений m2m — тоже тривиальная задача.
@Stefanov.sm: ты забыл следующую часть: "из списка компаний". То есть не сами пользователи, а список (вероятно, customers
), удерживаемый Company
.
Я думаю, что основная причина, по которой не хочется использовать необработанный SQL, заключается в том, что Django (в некоторой степени) не зависит от диалекта. Модели также могут в конечном итоге сопоставляться с таблицами с другим именем (вы можете переименовать таблицу в будущем), и даже находиться в нескольких базах данных.
@willeM_VanOnsem Все еще тривиально и гораздо понятнее delete from the_bridge_table tbd where exists (select from users where user_id = rbd.id and company_id = ?)
. Что касается использования и переносимости встроенного SQL, обратите внимание, что SQL является широко распространенным и устоявшимся стандартом, который поощряет хорошее проектирование данных, а Django - далеко не так.
@Stefanov.sm: Я не знаю Company.customers.through.objects.filter(user__active=False).delete()
точно говорит, что он делает, я думаю, у него есть эквивалент «шумовых» битов. Это работает, даже если таблица User
находится в другой базе данных. Если позже вы измените имя таблицы или столбцов базы данных, она все равно будет работать и будет работать со всеми типами баз данных (postgreSQL, mongoDB, MySQL и т. д.), поскольку она не зависит от диалекта.
@willeM_VanOnsem Насколько я понимаю, в данном конкретном случае нужно использовать правильный инструмент для работы, инструмент обработки данных для работы, ориентированной на данные. «Один размер подходит всем» в большинстве случаев работает, но выглядит это действительно не очень хорошо, не так ли?
@Stefanov.sm: ну, задача здесь - это веб-сервер, где администрирование компаний и пользователей является одной из задач, которые выполняют веб-серверы. Идея использования моделей заключается в том, что вы можете использовать их для десериализации данных, полученных из базы данных, автоматического создания и миграции таблиц вместе с этими моделями и т. д. Таким образом, работа заключается в создании моделей и ORM для взаимодействия с базой данных. Таким образом, эта административная задача представляет собой очень маленький шаг, и для ее выполнения потребуется также написать логику для открытия курсора и т. д.
Но более фундаментальная проблема SQL заключается в том, что он был разработан для взаимодействия с людьми, тогда как сегодня с ним разговаривают машины. Это не было первоначальной областью применения SQL, и поэтому SQL-инъекция никогда не ожидалась. Печально то, что многие веб-серверы по-прежнему используют необработанное форматирование SQL, что делает большую часть серверной инфраструктуры весьма уязвимой.
Но @willeM_VanOnsem, почему мы должны признать, что отсутствие образования является убедительным аргументом при обсуждении инженерного дела? Я очень сомневаюсь, что декларативный характер делает SQL предназначенным для взаимодействия с людьми. Это (и ORM также использует мотивацию) могло быть правдой в конце девяностых, но с тех пор прошло четверть века.
@Stefanov.sm: ну, я думаю, основная проблема в том, что объектно-ориентированная программа — это просто плохая идея. В свободное время я никогда не использую ОО-программирование: декларативный Haskell :). Но я нахожу аргумент «отсутствие образования» довольно странным. Вся идея разработки программного обеспечения состоит в том, чтобы сделать системы надежными, поэтому, если вам нужно больше образования, то явно что-то пошло не так. Основная идея чистых функциональных языков программирования, которая является ключевой, - это монады, что, таким образом, предотвращает появление побочных эффектов у функций. Не из-за хорошего образования, а просто потому, что конструкция языка этого не позволяет.
@willeM_VanOnsem Некоторые ваши идеи разделяю, другие - ну не так уж и много. В любом случае, хороший разговор.
Удалите элементы массово:
Company.customers.remove(*User.objects.filter(active=False))
При этом будут выполнены два запроса: один для получения первичных ключей (или поля, на которое ссылается ManyToManyField
) объектов User
, а затем массовое удаление всех этих элементов.
Если вы хотите сделать это для всех Company
, вы можете использовать:
# 🖟 reference to the Company model
Company.customers.through.objects.filter(user__active=False).delete()
если мы хотим работать с конкретным Company
, мы также можем немного ограничить количество запросов с помощью:
# 🖟 reference to the Company model
Company.customers.through.objects.filter(
company=some_company, user__active=False
).delete()
Разве это не успокоит всех пользователей? Несмотря на то, что мне просто нужны данные ПК (модель пользователя огромна, количество пользователей составит около 2-10 МБ).
@paul23: paul23: он сделает два обращения: одно для объектов User
, а затем одно для массового удаления всех элементов: github.com/django/django/blob/…
Если вы делаете это для многих компаний, вы можете повторно использовать набор запросов User
, тогда это будет один раз для Company
. Если вы хотите сделать это для всех компаний, мы можем ускорить процесс до двух запросов для всех Company
и User
.
making it really really slow
, «медленно» — это мнение, а не то, что можно измерить. Сколько времени на самом деле занимает один пользователь? А сколько пользователей нужно удалить? А где ваше приложение проводит время? Есть ли какой-нибудь индекс, который мог бы улучшить производительность?