我有一个有四个字段的模型。如何从我的数据库中删除重复的对象?
Daniel Roseman 对这个问题的回答似乎很合适,但我不确定如何将其扩展到每个对象有四个字段要比较的情况。
谢谢,
W。
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
# you can use `Min` if you wish to leave out the first record
to_delete = to_delete.exclude(id=duplicate["max_id"])
to_delete.delete()
你不应该经常这样做。改为对数据库使用unique_together
约束。
这留下了id
数据库中最大的记录。如果要保留原始记录(第一个),请使用models.Min
. 您还可以使用完全不同的字段,例如创建日期或其他内容。
底层 SQL
注释 django ORMGROUP BY
在查询中使用的所有模型字段上使用语句时。从而使用.values()
方法。GROUP BY
将对具有相同值的所有记录进行分组。重复的(不止一个id
for )稍后在on annotated生成的语句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()
可以肯定的是,.order_by()
在聚合之前添加一个空调用总是明智的QuerySet
。
用于排序的字段QuerySet
也包含在GROUP BY
语句中。Empty.order_by()
会覆盖模型Meta
中声明的列,因此它们不包含在 SQL 查询中(例如,按日期的默认排序可能会破坏结果)。
您目前可能不需要覆盖它,但有人可能会在稍后添加默认排序,从而破坏您宝贵的删除重复代码,甚至不知道这一点。是的,我确信你有 100% 的测试覆盖率……</p>
只需添加空.order_by()
以确保安全。;-)
交易
当然,您应该考虑在单个事务中完成所有操作。
https://docs.djangoproject.com/en/3.2/topics/db/transactions/#django.db.transaction.atomic