跨ManyToMany用related_name注解Django Queryset避免重复条目方法
解决方案
问题原因
你当前写法返回重复ListEntry对象,是因为F()跨多对多关系查询时会执行JOIN操作,每个关联的CompanySubType都会生成一行独立的查询结果,所以关联多个子类型的项目会对应多条重复的ListEntry记录。另外你代码中的关联路径list_pmatt__project_subtype是笔误,正确路径应为list_project__project_subtype。
方案1:使用ArrayAgg聚合(PostgreSQL环境优先选择,最简洁)
PostgreSQL数据库支持数组类型的聚合函数ArrayAgg,可以直接将多对多关联的多个值聚合成一个数组返回,不需要额外处理:
- 导入聚合函数
from django.db.models import ArrayAgg
- 改写查询逻辑
list_entry_qs = ListEntry.objects.prefetch_related( 'list_project', 'list_reviewer' ).annotate( # distinct=True避免出现重复的子类型值 subtypes=ArrayAgg('list_project__project_subtype__project_subtype', distinct=True) ).all()
注解得到的subtypes字段就是包含所有关联子类型的列表,直接传给DRF序列化器,用serializers.ListField(child=serializers.CharField())即可正常序列化。
方案2:序列化层处理(全数据库兼容)
如果你用的是MySQL等不支持ArrayAgg的数据库,可以通过预加载关联数据+序列化器自定义字段实现,性能和方案1一致:
- 优化查询逻辑,预加载关联的子类型数据,避免N+1查询
from django.db.models import Prefetch list_entry_qs = ListEntry.objects.prefetch_related( 'list_reviewer', # 预加载项目关联的所有子类型 Prefetch('list_project', queryset=CompanyProject.objects.prefetch_related('project_subtype')) ).all()
- 为ListEntry序列化器新增子类型字段
from rest_framework import serializers class ListEntrySerializer(serializers.ModelSerializer): subtypes = serializers.SerializerMethodField() class Meta: model = ListEntry fields = ['id', 'list_project', 'list_reviewer', 'subtypes'] def get_subtypes(self, obj): # 直接调用模型中已定义的方法返回子类型列表 return obj.list_project.get_project_subtypes()
内容的提问来源于stack exchange,提问作者RiverRook
相关产品推荐
相关产品推荐

