如何在Django ORM中使用Group by子句且不包含模型ID?
问题描述
模型定义:
class Model1(models.Model): category = models.CharField() name = models.CharField()
想要统计每个category的数量,编写了如下ORM语句:
from django.db.models import Count result = Model1.objects.values('category').annotate(Count('sns_type'))
得到的查询结果:
<QuerySet [{'category': 'A', 'category__count': 1}, {'category': 'B', 'category__count': 1},...]>
打印对应的SQL语句,发现自动包含了id的分组:
SELECT "example_table"."category", COUNT("example_table"."category") AS "category__count" FROM "example_table" GROUP BY "example_table"."category", "example_table"."id"
期望的SQL语句应该是:
SELECT "example_table"."category", COUNT("example_table"."category") AS "category__count" FROM "example_table" GROUP BY "example_table"."category"
需要实现不包含模型ID的GROUP BY分组。
解决方法
原因分析
Django的QuerySet默认会添加基于模型主键(id)的排序规则,而SQL标准要求GROUP BY子句必须包含所有非聚合的SELECT字段以及排序字段,因此id会被自动加入GROUP BY中。另外你的ORM语句里Count('sns_type')存在错误——模型中并没有sns_type字段,Django会自动 fallback 到统计主键id,这也间接加剧了这个问题。
修正后的ORM写法
- 修正聚合字段:将
Count('sns_type')改为正确的字段,比如统计主键使用Count('id'),或者统计category本身用Count('category'),也可以用Count('*')统计分组内的总条数。 - 清空默认排序:在QuerySet末尾添加
.order_by(),去掉Django自动添加的主键排序规则,这样GROUP BY就只会包含你指定的category字段。
最终代码:
from django.db.models import Count # 统计每个category的数量,GROUP BY仅包含category result = Model1.objects.values('category').annotate(count=Count('id')).order_by()
对应的SQL语句会变成你期望的样子:
SELECT "example_table"."category", COUNT("example_table"."id") AS "count" FROM "example_table" GROUP BY "example_table"."category"
额外说明
如果使用的是Django 3.2及以上版本,也可以通过修改模型的Meta类中的ordering属性,去掉默认的主键排序,但这种方式会影响整个模型的所有QuerySet,不如在单个查询中使用.order_by()更灵活。
内容的提问来源于stack exchange,提问作者Universpace
相关产品推荐
相关产品推荐

