如何利用Django特性查找Foobar模型中两个外键字段的重复数据?
Django 高效查找Foobar重复记录的方法
你当前用嵌套循环的方式效率极低,会触发大量不必要的数据库查询。Django ORM 提供了聚合分组的特性,能直接在数据库层面完成重复记录的查找,性能提升非常明显。
方式一:先找重复组合,再获取对应记录
通过对field_foo和field_bar分组,统计每组的记录数,筛选出数量大于1的组合,再基于这些组合获取所有重复的Foobar行:
from django.db.models import Count # 第一步:找出所有重复的(field_foo, field_bar)组合及重复次数 duplicate_groups = Foobar.objects.values('field_foo', 'field_bar') \ .annotate(rec_count=Count('id')) \ .filter(rec_count__gt=1) # 第二步:根据组合查询所有重复记录 duplicate_records = Foobar.objects.filter( models.Q(field_foo__in=[g['field_foo'] for g in duplicate_groups]) & models.Q(field_bar__in=[g['field_bar'] for g in duplicate_groups]) ).order_by('field_foo', 'field_bar')
方式二:直接查询所有重复行(子查询优化)
如果想一步到位获取所有重复记录,可以用子查询关联每组的计数:
from django.db.models import Subquery, OuterRef # 子查询:统计当前记录所属组的总数量 group_count = Foobar.objects.filter( field_foo=OuterRef('field_foo'), field_bar=OuterRef('field_bar') ).values('field_foo', 'field_bar').annotate(count=Count('id')).values('count') # 获取所有重复的记录 duplicates = Foobar.objects.annotate( total=Subquery(group_count) ).filter(total__gt=1)
额外提示:从根源避免重复
如果不想后续再处理这类重复数据,建议给Foobar模型添加联合唯一约束,数据库层面直接阻止重复组合插入:
class Foobar(models.Model): field_foo = models.ForeignKey(Foo, on_delete=models.CASCADE) # 注意你原代码里写的小写foo要改为大写Foo field_bar = models.ForeignKey(Bar, on_delete=models.CASCADE) # 同理小写bar改为大写Bar class Meta: # Django 2.2+推荐用constraints定义约束 constraints = [ models.UniqueConstraint( fields=['field_foo', 'field_bar'], name='unique_foo_bar_pair' ) ]
内容的提问来源于stack exchange,提问作者Yotam
相关产品推荐
相关产品推荐

