Django ORM自动添加未指定字段至GROUP BY的原因及解决方法
问题1:为什么Django ORM会自动将name添加到GROUP BY子句?
这是因为你的Transition模型在Meta类里设置了ordering = ('name', )——Django ORM的一个默认行为是:当你使用annotate()进行分组查询时,如果模型有默认排序字段,它会自动把这些排序字段加入到GROUP BY子句中。
背后的原因是:大多数数据库(比如PostgreSQL)要求ORDER BY中的字段要么出现在GROUP BY里,要么是聚合函数的结果。Django为了避免数据库抛出错误,会自动把默认排序的字段追加到GROUP BY中,哪怕你没显式指定。这就是为什么你看到的SQL里多了"wcm_workflows_transition"."name"。
问题2:如何通过ORM实现你预期的查询?
要解决这个问题,我们需要两步:
- 清除默认排序的影响:使用
.order_by()(不带参数)来覆盖模型的默认排序,这样Django就不会把name加到GROUP BY里了。 - 实现数组内元素排序:你的预期SQL里写了
ORDER BY "wcm_workflows_transition"."name" ASC,但实际上这个排序放在GROUP BY之后是不合法的(因为name不在分组字段里),推测你真正想要的是让聚合出来的数组里的name按升序排列,这可以通过ArrayAgg的ordering参数实现。
最终的ORM代码如下:
from django.db.models import ArrayAgg Transition.objects.values("workflow__id").annotate( names=ArrayAgg("name", ordering=("name",)) ).values("names").order_by()
这段代码生成的SQL逻辑完全符合你的预期:
- 仅按
workflow_id分组 - 聚合出的
names数组内的元素按name升序排列 - 不会额外把
name加到GROUP BY子句中
如果你的需求确实是对整个结果集排序,可以调整为按workflow_id或者聚合后的names字段排序,比如追加.order_by('workflow__id')。
内容的提问来源于stack exchange,提问作者Louis Saglio
相关产品推荐
相关产品推荐

