You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DRF使用Annotate查询作者有书在售的所有门店仅返回单值如何解决

问题原因

  1. F 表达式的作用是引用数据库字段值,不会做聚合处理。跨多层多对多关系查询时,会为每一个「作者-书籍-门店」的关联行生成一条独立的作者记录,每条记录仅对应一个门店名称,你拿到的只是第一条记录的门店值。
  2. 序列化器中 stores 字段定义为 CharField,只能接收单个字符串值,即使查询返回了多个结果也无法正确解析为列表。

解决方案

方案1:使用聚合函数(性能最优,推荐)

根据你使用的数据库选择对应聚合函数:

  • 如果你用的是PostgreSQL,可直接使用Django内置的ArrayAgg函数,直接将多个门店名称聚合为数组:
    首先导入聚合类:
from django.db.models import ArrayAgg

修改get_queryset方法:

def get_queryset(self):
    qs = Author.objects.all().annotate(
        # distinct=True 避免同一门店因关联多本作者书籍重复出现
        stores=ArrayAgg('books__stores__name', distinct=True)
    )
    return qs

同时修改序列化器的stores字段类型,适配数组格式:

class AuthorSerializer(ModelSerializer):
    stores = serializers.ListField(child=serializers.CharField(max_length=300))

    class Meta:
        model = Author
        fields = ['name', 'age', 'books', 'stores']
  • 如果你用的是MySQL,可使用GroupConcat函数将门店名称拼接为逗号分隔的字符串,后续可自行拆分为列表:
    导入聚合类:
from django.db.models import GroupConcat

修改get_queryset方法:

def get_queryset(self):
    qs = Author.objects.all().annotate(
        stores=GroupConcat('books__stores__name', distinct=True)
    )
    return qs

方案2:使用SerializerMethodField(适配性更强,适合小数据量场景)

不需要修改查询逻辑,直接在序列化器中处理关联查询,搭配预加载避免N+1问题:
修改get_queryset方法,预加载关联数据:

def get_queryset(self):
    return Author.objects.prefetch_related('books__stores').all()

修改序列化器:

class AuthorSerializer(ModelSerializer):
    stores = serializers.SerializerMethodField()

    def get_stores(self, obj):
        # 去重后返回门店名称列表
        return list(obj.books.values_list('stores__name', flat=True).distinct())

    class Meta:
        model = Author
        fields = ['name', 'age', 'books', 'stores']

内容的提问来源于stack exchange,提问作者Leonardo Furtado

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 09:45:07