Как найти дубликаты и деактивировать дубликаты для атрибутов пользователя
Предположим, что у нас есть модель в django, определенная следующим образом:
class DateClass:
user_id = models.IntegerField(...)
sp_date = models.DateField(...)
is_active = models.BooleanField(...)
...
Здесь я следую политике вставки, т.е. для конкретного пользователя будет только одна конкретная активная дата. Это означает, что для пользователя=1 в таблице date будет только одна активная строка для значений sp_date 27/10/2021, 28/10/2021 и так далее. Не должно быть двух активных строк для 27/10/2021 для user=1, но для других пользователей должны быть строки для 27/10/2021. Когда дата должна быть обновлена, я деактивирую (is_active=False) предыдущий ряд и добавляю новый ряд для конкретной даты.
Я хочу найти дубликаты активных дат для каждого пользователя в одном запросе, а затем деактивировать (установить is_active=False) все дублирующие значения, кроме последнего ряда (ряд, который был вставлен последним). Два ряда будут дублироваться, если значения user_id и sp_date равны и оба имеют is_active=True. Я знаю, как найти дубликаты для определенного столбца, что довольно просто. Но я не могу придумать что-то, что могло бы элегантно решить вышеуказанную задачу. Я могу думать только о следующем подходе:
for user in users:
dates = DateClass(user_id=user.id, is_active=True)
for date in dates:
days = dates.filter(
sp_date=date.sp_date, is_active=True
)
if days.count() > 1:
last_day = days.last()
days.exclude(id=last_day.id).update(is_active=False)
Как вы можете видеть выше, один вариант не очень эффективен, так как мне приходится перебирать всех пользователей. Есть ли способ сделать это более эффективно? Я использую PostgreSQL для базы данных.
Вот отличный ответ на вопрос о нескольких дублирующихся полях, полученный из этого ответа, поскольку я не хочу присваивать себе эту заслугу, а также не хочу изобретать колесо, поэтому я предложу этот ответ
Для вашего случая это должно быть:
from django.db.models import Max, Count
duplicate_date_class = DateClass.objects.values('user_id', 'sp_date') \
.annotate(records=Count('user_id')) \
.filter(records__gt=1)
# Then do operations on duplicates
for date_class in duplicate_date_class:
DateClass.objects.filter(
user_id=date_class['user_id'],
sp_date=date_class['sp_date']
)[1:].update(is_active=False)
Если вы хотите избежать дублирования множества полей, я предлагаю взглянуть на unique_together для проверки модели