Django StringAgg搭配条件表达式使用distinct报错的原因与解决方法
问题原因及解决方案
问题原因
PostgreSQL有个硬性规则:当聚合函数用DISTINCT去重时,ORDER BY里的所有表达式必须直接出现在聚合函数的参数列表里。
第一个示例里,StringAgg的参数是"credits__role__name",ordering也指定了同一个字段,完全符合规则,所以能正常运行。
第二个示例就不一样了:StringAgg的参数是Case(When(...))这个条件表达式,但ordering写的是"credits__entity__name"——这个字段只是嵌套在Case里面,并没有直接作为聚合参数存在,PostgreSQL不认它属于聚合参数的一部分,所以直接抛出ProgrammingError。
可行解决方案
方案一:用FilteredRelation先过滤再聚合
先通过FilteredRelation把符合类型要求的credits筛选出来,再对筛选后的关联字段做StringAgg,这样就和第一个示例的常规用法一致,不会触发PostgreSQL的限制:
from django.db.models import FilteredRelation, Q if request.GET.get("order[0][name]"): order = request.GET["order[0][name]"] if order == "credits_primary": releases = releases.annotate( primary_credits=FilteredRelation( 'credits', condition=Q(credits__type="primary") ) ).annotate( credits_primary=StringAgg( 'primary_credits__entity__name', delimiter=", ", distinct=True, ordering='primary_credits__entity__name' ) ) elif order == "credits_secondary": releases = releases.annotate( secondary_credits=FilteredRelation( 'credits', condition=Q(credits__type="secondary") ) ).annotate( credits_secondary=StringAgg( 'secondary_credits__entity__name', delimiter=", ", distinct=True, ordering='secondary_credits__entity__name' ) ) # 后续排序代码保持不变
方案二:让ordering和聚合参数用同一个Case表达式
把ordering设置成和聚合参数完全一样的Case表达式,确保排序用的表达式属于聚合参数的一部分,同时指定output_field让Django生成正确的SQL:
from django.db.models import Case, When, Value, CharField if request.GET.get("order[0][name]"): order = request.GET["order[0][name]"] if order == "credits_primary": # 定义可复用的Case表达式 primary_case = Case( When(credits__type="primary", then="credits__entity__name"), default=Value(None), output_field=CharField() ) releases = releases.annotate(credits_primary=StringAgg( primary_case, delimiter=", ", distinct=True, ordering=primary_case )) elif order == "credits_secondary": secondary_case = Case( When(credits__type="secondary", then="credits__entity__name"), default=Value(None), output_field=CharField() ) releases = releases.annotate(credits_secondary=StringAgg( secondary_case, delimiter=", ", distinct=True, ordering=secondary_case )) # 后续排序代码保持不变
说明:方案一逻辑更清晰,可读性更高,优先推荐;方案二则不需要额外的关联过滤,直接通过调整表达式满足规则。
内容的提问来源于stack exchange,提问作者bur
相关产品推荐
相关产品推荐

