最佳答案
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
将所有具有相同值的记录分组。重复的(多个 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
中被删除循环,每个组中出现频率最高的异常(exception)。
空 .order_by()
可以肯定的是,添加一个空的 .order_by()
总是明智的在聚合之前调用 QuerySet
.
用于订购 QuerySet
的字段也包含在 GROUP BY
中陈述。空 .order_by()
覆盖模型的 Meta
中声明的列结果它们不包含在 SQL 查询中(例如,默认按日期排序会破坏结果)。
您目前可能不需要覆盖它,但稍后可能有人会添加默认顺序,因此毁了您宝贵的删除重复代码,甚至都不知道。是的,我确定您有 100% 的测试覆盖率……
只需添加空.order_by()
为了安全。 ;-)
交易
当然,您应该考虑在单个事务中完成所有操作。
https://docs.djangoproject.com/en/3.2/topics/db/transactions/#django.db.transaction.atomic
关于django - 删除 Django ORM 中的重复项——多行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13700200/