Удалить повторяющиеся строки в Django DB

У меня есть модель, где из-за ошибки кода есть повторяющиеся строки. Теперь мне нужно удалить любые дубликаты из базы данных.

Каждая строка должна иметь уникальную фотографию_ид. Есть ли простой способ их удалить? Или мне нужно сделать что-то вроде этого:

rows = MyModel.objects.all()
for row in rows:
    try:
        MyModel.objects.get(photo_id=row.photo_id)
    except:
        row.delete()

Ответ 1

Самый простой способ - самый простой способ! Особенно для одного сценария, где производительность даже не имеет значения (если только это не так). Поскольку это не основной код, я бы просто написал первое, что приходит на ум и работает.

# assuming which duplicate is removed doesn't matter...
for row in MyModel.objects.all():
    if MyModel.objects.filter(photo_id=row.photo_id).count() > 1:
        row.delete()

Как всегда, создайте резервную копию, прежде чем делать это.

Ответ 2

Это может быть быстрее, потому что это позволяет избежать внутреннего фильтра для каждой строки в MyModel.

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

lastSeenId = float('-Inf')
rows = MyModel.objects.all().order_by('photo_id')

for row in rows:
  if row.photo_id == lastSeenId:
    row.delete() # We've seen this id in a previous row
  else: # New id found, save it and check future rows for duplicates.
    lastSeenId = row.photo_id 

Ответ 3

Вот быстрое решение:

from django.db import connection

query = "SELECT id FROM table_name GROUP BY unique_column HAVING COUNT(unique_column)>1"
cursor = connection.cursor()
cursor.execute(query)
ids_list = [item[0] for item in cursor.fetchall()]

теперь вы можете сделать:

Some_Model.objects.filter(id__in=ids_list).delete()

или если ids_list слишком велико, чтобы обрабатывать ваши dbms

вы можете сегментировать его на куски, которые могут быть обработаны им:

seg_length = 100
ids_lists = [ids_list[x:x+seg_length] for x in range(0,len(ids_list),seg_length)]
for ids_list in ids_lists:
    SomeModel.objects.filter(id__in=ids_list).delete()

Ответ 4

Вместо перебора всей таблицы вы можете просто сделать

count = MyModel.objects.filter(photo_id='some_photo_id').count()
while count >=1:
    MyModel.objects.filter(photo_id='some_photo_id')[0].delete()
    count -= 1

Ответ 5

Общий и оптимизированный метод в случае необходимости удаления большого количества объектов -

qs = Model.objects.all()
key_set = set()
delete_ids_list = []
for object in qs:
    object_key = object.unique_key    # photo_id here
    if object_key in key_set:
        delete_ids_list.append(object.id)
    else:
        key_set.add(object_key)
Model.objects.filter(id__in=delete_ids_list).delete()