Массовое удаление записей типа «многие ко многим»?

Предположим, у меня тысячи пользователей, эти пользователи используются в отношениях «многие ко многим» с «компаниями».

Теперь я хочу удалить всех «неактивных» пользователей (а их большинство) из списка компаний.

Есть трюк, который я мог бы использовать с remove:

for user in User.filter(active=False):
    Company.customers.remove(user)

Однако это также приведет к тысячам обращений к базе данных, что сделает процесс очень медленным. Есть ли способ сделать это в Джанго? Не заходя в rawsql?

making it really really slow, «медленно» — это мнение, а не то, что можно измерить. Сколько времени на самом деле занимает один пользователь? А сколько пользователей нужно удалить? А где ваше приложение проводит время? Есть ли какой-нибудь индекс, который мог бы улучшить производительность?
Frank Heikens 04.04.2024 16:20

Эта команда выполняется очень редко, однако она разрывает соединение django/azure с базами данных клиентов. Из-за конфиденциальности я не могу предоставить (и не знаю как разработчик) точные детали, только сервер регистрирует, что это занимает более 10 минут. сделать это для каждой компании для каждого пользователя.

paul23 04.04.2024 16:29

Почему «Без перехода в rawsql»?

Stefanov.sm 04.04.2024 17:09
that it takes 10+ minutes to do this for every company for every user Удаление одного пользователя занимает более 10 минут? Я бы сказал, что 10 секунд — это очень долго, но 10 минут — это просто потрясающе. Похоже, у вас проблемы посерьёзнее, чем просто это удаление. Просто удаление показывает вам проблему. Проверьте свои запросы, проверьте планы запросов и улучшите свои стратегии индексирования. Похоже, есть что выиграть.
Frank Heikens 04.04.2024 17:45

Всю эту «опу-буффу» можно свести к вот этому просто, быстро и понятно delete from users where not active and company_id = ? cascade.

Stefanov.sm 04.04.2024 18:20

@FrankHeikens, конечно, не один пользователь, он удаляет тысячи полей пользователей-компаний. И я особенно заметил, что метод получения для пользователей получает много дополнительных данных.

paul23 05.04.2024 09:05

@Stefanov.sm: Stefanov.sm: Я думаю, что следует удалять не самих неактивных пользователей, а отношения m2m, которые их связывают.

willeM_ Van Onsem 05.04.2024 09:10

@willeM_VanOnsem Хорошо, процитируйте ОП: «Я хочу удалить всех «неактивных» пользователей (а это большинство) из списка компаний» (выделено жирным шрифтом). Однако удаление только отношений m2m — тоже тривиальная задача.

Stefanov.sm 05.04.2024 18:20

@Stefanov.sm: ты забыл следующую часть: "из списка компаний". То есть не сами пользователи, а список (вероятно, customers), удерживаемый Company.

willeM_ Van Onsem 05.04.2024 18:25

Я думаю, что основная причина, по которой не хочется использовать необработанный SQL, заключается в том, что Django (в некоторой степени) не зависит от диалекта. Модели также могут в конечном итоге сопоставляться с таблицами с другим именем (вы можете переименовать таблицу в будущем), и даже находиться в нескольких базах данных.

willeM_ Van Onsem 05.04.2024 18:29

@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 05.04.2024 18:29

@Stefanov.sm: Я не знаю Company.customers.through.objects.filter(user__active=False)‌​.delete() точно говорит, что он делает, я думаю, у него есть эквивалент «шумовых» битов. Это работает, даже если таблица User находится в другой базе данных. Если позже вы измените имя таблицы или столбцов базы данных, она все равно будет работать и будет работать со всеми типами баз данных (postgreSQL, mongoDB, MySQL и т. д.), поскольку она не зависит от диалекта.

willeM_ Van Onsem 05.04.2024 18:31

@willeM_VanOnsem Насколько я понимаю, в данном конкретном случае нужно использовать правильный инструмент для работы, инструмент обработки данных для работы, ориентированной на данные. «Один размер подходит всем» в большинстве случаев работает, но выглядит это действительно не очень хорошо, не так ли?

Stefanov.sm 05.04.2024 18:38

@Stefanov.sm: ну, задача здесь - это веб-сервер, где администрирование компаний и пользователей является одной из задач, которые выполняют веб-серверы. Идея использования моделей заключается в том, что вы можете использовать их для десериализации данных, полученных из базы данных, автоматического создания и миграции таблиц вместе с этими моделями и т. д. Таким образом, работа заключается в создании моделей и ORM для взаимодействия с базой данных. Таким образом, эта административная задача представляет собой очень маленький шаг, и для ее выполнения потребуется также написать логику для открытия курсора и т. д.

willeM_ Van Onsem 05.04.2024 18:42

Но более фундаментальная проблема SQL заключается в том, что он был разработан для взаимодействия с людьми, тогда как сегодня с ним разговаривают машины. Это не было первоначальной областью применения SQL, и поэтому SQL-инъекция никогда не ожидалась. Печально то, что многие веб-серверы по-прежнему используют необработанное форматирование SQL, что делает большую часть серверной инфраструктуры весьма уязвимой.

willeM_ Van Onsem 05.04.2024 18:43

Но @willeM_VanOnsem, почему мы должны признать, что отсутствие образования является убедительным аргументом при обсуждении инженерного дела? Я очень сомневаюсь, что декларативный характер делает SQL предназначенным для взаимодействия с людьми. Это (и ORM также использует мотивацию) могло быть правдой в конце девяностых, но с тех пор прошло четверть века.

Stefanov.sm 05.04.2024 18:52

@Stefanov.sm: ну, я думаю, основная проблема в том, что объектно-ориентированная программа — это просто плохая идея. В свободное время я никогда не использую ОО-программирование: декларативный Haskell :). Но я нахожу аргумент «отсутствие образования» довольно странным. Вся идея разработки программного обеспечения состоит в том, чтобы сделать системы надежными, поэтому, если вам нужно больше образования, то явно что-то пошло не так. Основная идея чистых функциональных языков программирования, которая является ключевой, - это монады, что, таким образом, предотвращает появление побочных эффектов у функций. Не из-за хорошего образования, а просто потому, что конструкция языка этого не позволяет.

willeM_ Van Onsem 06.04.2024 09:47

@willeM_VanOnsem Некоторые ваши идеи разделяю, другие - ну не так уж и много. В любом случае, хороший разговор.

Stefanov.sm 06.04.2024 22:19
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
18
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Удалите элементы массово:

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 04.04.2024 15:52

@paul23: paul23: он сделает два обращения: одно для объектов User, а затем одно для массового удаления всех элементов: github.com/django/django/blob/…

willeM_ Van Onsem 04.04.2024 15:54

Если вы делаете это для многих компаний, вы можете повторно использовать набор запросов User, тогда это будет один раз для Company. Если вы хотите сделать это для всех компаний, мы можем ускорить процесс до двух запросов для всех Company и User.

willeM_ Van Onsem 04.04.2024 15:59

Другие вопросы по теме