答案 0 :(得分:66)
def remove_duplicated_records(model, fields):
"""
Removes records from `model` duplicated on `fields`
while leaving the most recent one (biggest `id`).
"""
duplicates = model.objects.values(*fields)
# override any model specific ordering (for `.annotate()`)
duplicates = duplicates.order_by()
# group by same values of `fields`; count how many rows are the same
duplicates = duplicates.annotate(
max_id=models.Max("id"), count_id=models.Count("id")
)
# leave out only the ones which are actually duplicated
duplicates = duplicates.filter(count_id__gt=1)
for duplicate in duplicates:
to_delete = model.objects.filter(**{x: duplicate[x] for x in fields})
# leave out the latest duplicated record
to_delete = to_delete.exclude(id=duplicate["max_id"])
to_delete.delete()
你不应该经常这样做。而是在数据库上使用unique_together
约束。
这会在DB中留下最大id
的记录。如果要保留原始记录(第一个),请使用models.Min
稍微修改一下代码。您还可以使用完全不同的字段,例如创建日期等。
基础SQL代码
当注释django ORM在查询中使用的所有模型字段上使用GROUP BY
语句时。因此使用.values()
方法。 GROUP BY
会将具有相同值的所有记录分组。重复的(id
的多个unique_fields
)稍后会在注释HAVING
上由.filter()
生成的QuerySet
语句中过滤掉。
SELECT
field_1,
…
field_n,
MAX(id) as max_id,
COUNT(id) as count_id
FROM
app_mymodel
GROUP BY
field_1,
…
field_n
HAVING
count_id > 1
稍后在for
循环中删除重复的记录,但每组最常见的记录除外。
清空。订单_()
为了确保,在汇总.order_by()
之前添加空QuerySet
次呼叫总是明智的。
用于排序QuerySet
的字段也包含在GROUP BY
语句中。空.order_by()
会覆盖模型Meta
中声明的列,结果它们不包含在SQL查询中(例如,按日期默认排序会破坏结果)。
您可能不需要在当前时刻覆盖它,但有些人可能会在以后添加默认排序,因此破坏您珍贵的删除重复代码甚至不知道。是的,我相信你有100%的测试覆盖率......
只需添加空.order_by()
即可。 ; - )
<强>交易强>
当然,您应该考虑在一次交易中完成所有工作。
https://docs.djangoproject.com/en/1.11/topics/db/transactions/#django.db.transaction.atomic