Django中annotate关联子表查询返回重复条目该如何解决?
问题原因
你触发的是Django ORM关联查询的常见行为:当你在annotate中直接引用反向外键的字段(也就是subitems__status)时,ORM会自动执行LEFT JOIN把Item表和SubItem表关联,有多少个关联的SubItem,就会生成多少行主表记录,这就是你看到重复条目、count_subitems统计错误的原因。
解决方案1:使用Exists子查询(推荐,Django 3.0+支持)
这种方案不会产生JOIN操作,不会出现重复行,性能也更好:
from django.db.models import Exists, OuterRef class ItemQuerySet(models.QuerySet): def annotate_subitem_stats(self): # 构造子查询:判断当前Item是否有关联的status为sent的SubItem has_sent_subquery = SubItem.objects.filter( item=OuterRef('pk'), status=Status.sent ) return self.annotate( count_subitems=Count('subitems'), has_sent_subitems=Exists(has_sent_subquery) )
解决方案2:使用聚合函数兼容旧版本Django
如果你的Django版本低于3.0不支持Exists,可以用聚合函数按主表分组,避免重复行:
from django.db.models import Sum, Case, When, BooleanField from django.db.models.functions import Cast class ItemQuerySet(models.QuerySet): def annotate_subitem_stats(self): return self.annotate( # 加distinct=True避免JOIN导致的计数重复 count_subitems=Count('subitems', distinct=True), has_sent_subitems=Cast( Sum( Case( When(subitems__status=Status.sent, then=1), default=0 ) ), output_field=BooleanField() ) )
说明:只要存在至少一个sent状态的SubItem,Sum的结果就≥1,转为布尔值就是True。聚合函数会自动按Item的主键分组,所以每个Item只会返回一条记录。
内容的提问来源于stack exchange,提问作者keyvanm
相关产品推荐
相关产品推荐

