Django中如何通过子模型为父模型正确添加Annotate字段
解决Django DRF中Annotate关联字段导致父模型重复的问题
问题背景
现有两个关联模型AA和BB,需求是获取AA的列表,为每个AA对象添加status字段:当该AA关联的BB中存在name2等于客户端传入的word时,status为True,否则为False。但现有实现导致AA条目重复。
模型定义
class AA(models.Model): name = models.CharField(max_length=255) # Django CharField需指定max_length class BB(models.Model): parent = models.ForeignKey(AA, related_name="BB", on_delete=models.CASCADE) # 外键需指定on_delete name2 = models.CharField(max_length=255)
预期输出
[ { "name": "aaa1", "status": true }, { "name": "aaa2", "status": false } ]
现有代码(存在重复问题)
queryset = (AA.objects.prefetch_related("BB").annotate( status=Case( When(BB__name2=word, then=Value(True)), default=Value(False), output_field=BooleanField() ) ))
错误返回结果
[ { "name": "aaa1", "status": true }, { "name": "aaa1", "status": false }, { "name": "aaa1", "status": false }, { "name": "aaa2", "status": true } ]
解决方案
问题根源是使用BB__name2进行条件判断时,Django会执行表JOIN,每个匹配的BB记录都会对应一条AA结果,导致重复。正确的做法是使用Exists子查询,针对每个AA单独判断是否存在满足条件的BB记录,不会产生重复。
修正后的代码
from django.db.models import Exists, OuterRef queryset = AA.objects.annotate( status=Exists( BB.objects.filter( parent=OuterRef('pk'), name2=word ) ) )
说明
Exists子查询会为每个AA对象执行一次独立判断,直接返回布尔值,不会触发表JOIN导致的重复条目。- 若无需获取
BB的具体数据,可去掉prefetch_related("BB"),减少不必要的数据库查询开销。 - 如果需要同时加载
BB数据,可保留prefetch_related("BB"),不会影响status字段的正确性。
内容的提问来源于stack exchange,提问作者chocho
相关产品推荐
相关产品推荐

