【发布时间】:2012-12-04 09:57:56
【问题描述】:
我有一个有四个字段的模型。如何从我的数据库中删除重复的对象?
Daniel Roseman 对this question 的回答似乎很合适,但我不确定如何将其扩展到每个对象有四个字段要比较的情况。
谢谢,
W.
【问题讨论】:
标签: django django-models
我有一个有四个字段的模型。如何从我的数据库中删除重复的对象?
Daniel Roseman 对this question 的回答似乎很合适,但我不确定如何将其扩展到每个对象有四个字段要比较的情况。
谢谢,
W.
【问题讨论】:
标签: django django-models
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 ORM 在查询中使用的所有模型字段上使用GROUP BY 语句时。因此使用.values()方法。 GROUP BY 将对具有相同值的所有记录进行分组。重复的(unique_fields 不止一个id)稍后在.filter() 对注释QuerySet 生成的HAVING 语句中过滤掉。
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 之前添加一个空的 .order_by() 调用总是明智的。
用于排序QuerySet 的字段也包含在GROUP BY 语句中。空的 .order_by() 会覆盖模型的 Meta 中声明的列,因此它们不包含在 SQL 查询中(例如,默认的按日期排序可能会破坏结果)。
您目前可能不需要覆盖它,但有人可能会在以后添加默认排序,因此会破坏您宝贵的删除重复代码,甚至不知道这一点。是的,我确信你有 100% 的测试覆盖率……
只需添加空的.order_by() 以确保安全。 ;-)
交易
当然,您应该考虑在单个事务中完成所有操作。
https://docs.djangoproject.com/en/3.2/topics/db/transactions/#django.db.transaction.atomic
【讨论】:
MyModel.objects.values(*unique_fields) 会生成一组字典,每个字典都与一个对象有关。但后来我迷路了——注释在做什么?
NameError: name 'duplicate' is not defined (Python3.4, Django1.11)。这对我有用:Role.objects.filter(**{field_1: d[field_1], ..., field_n: d[field_n]}).exclude(id=d['max_id']).delete()。出于某种原因,它不想从过滤器语句中的重复变量中解压缩它:/