Django查询集多字段聚合注解时Subquery返回多列报错如何解决
解决方案
方案1:聚合为JSONB实现动态分类过滤
你之前的报错是因为Subquery要求返回单列结果,你需要先在子查询内把多行列的分类计数聚合成单个JSONB字段再返回,不需要额外套Cast:
from django.db.models import Count, OuterRef from django.contrib.postgres.aggregates import JSONBAgg # 构造子查询 genre_count_subquery = Author.objects.filter( id=OuterRef("pk") ).values("id") # 按当前关联的作者ID分组 .annotate( # 将分类名称和对应计数聚合成键值对格式的JSONB genre_counts=JSONBAgg( "main_books__genre", Count("main_books__genre") ) ).values("genre_counts") # 仅返回单列JSONB,符合Subquery入参要求 # 给主查询集添加注解 annotated_queryset = author_queryset.annotate( genre_counts=genre_count_subquery )
注解完成后即可直接按你的需求过滤:
# 筛选科幻类作品不少于5本的作者 annotated_queryset.filter(genre_counts__scifi__gte=5)
如果你的Django版本不支持JSONBAgg直接生成键值对,可以用原生SQL函数拼接JSON:
from django.db.models import F, Func, Value genre_count_subquery = Author.objects.filter( id=OuterRef("pk") ).values("id", "main_books__genre") .annotate(cnt=Count("main_books__genre")) .annotate( kv=Func(Value('"'), F("main_books__genre"), Value('":'), F("cnt"), function="concat") ).values("id") .annotate( genre_counts=Func(Value("{"), Func(F("kv"), function="array_to_string"), Value("}"), function="concat") ).values("genre_counts")
方案2:条件注解(性能更高,推荐提前知道过滤维度时使用)
如果你提前知道需要过滤的分类维度,不需要返回全部分类的计数,直接用条件注解即可,不需要依赖PostgreSQL专属特性,性能也更好:
from django.db.models import Count, Q annotated_queryset = author_queryset.annotate( scifi_count=Count("main_books", filter=Q(main_books__genre="scifi")), fantasy_count=Count("main_books", filter=Q(main_books__genre="fantasy")), mystery_count=Count("main_books", filter=Q(main_books__genre="mystery")) ) # 过滤示例 filtered_authors = annotated_queryset.filter(scifi_count__gte=5)
内容的提问来源于stack exchange,提问作者lys
相关产品推荐
相关产品推荐

