解决
由于在 model 中的 meta 添加了默认的 order_by,导致使用 annotate 时一直无法满足预期的结果,只需要添加空的 order_by 重置排序,或者删去默认排序即可。
过程
在 django 中,使用 anotate 来做分组查询。以下是对 annotate 的介绍以及为什么我会出现问题的描述。
假设我们有这么一张表 table1:
id | 用户名 username | 系统 system |
---|---|---|
1 | 张三 | Windows |
2 | 李四 | Android |
3 | 王五 | IOS |
4 | 赵六 | Windows |
如果我们要对统计有多少人在使用使用这些系统,只需要这么一小句 SQL 语句
select system, count(device) from table1 group by system;
输出如下:
+---------+---------------+
| device | count(device) |
+---------+---------------+
| Android | 1 |
| IOS | 1 |
| Windows | 2 |
+---------+---------------+
而在 django 中理论上只需要这样即可完成上述操作(假设 Table1 为模型类):
from django.db.models import Count
queryset = Table1.objects.values("device").annotate(Count("device"))
然而事实并不如我所愿,在语句执行之后,发现目标字段并没有进行分组,而是单独分开成了一个组,结果大概就像下面这样,所以 Count(“device”) 就变成了 1。
+---------+---------------+
| device | count(device) |
+---------+---------------+
| Android | 1 |
| IOS | 1 |
| Windows | 1 |
| Windows | 1 |
+---------+---------------+
将 queryset 的原始对象打印,发现该语句多了一个用于 group by 的字段,大概如下:
>>> print(queryset.query)
SELECT `table1`.`device`, COUNT(`table1`.`device`) FROM `table1` GROUP BY `table1`.`device`, `table1`.`create_time`
可以看到是 group by 之后多了 create_time 字段,这个字段是我在创建模型类的时候填写的默认排序,即 class meta 中的 ordering。在翻阅了不少东西过后,发现解决它就得从排序下手,要么删除默认排序,要么使用一个空的排序来重置它。于是上面的 django 代码可以改写成这样。
queryset = Table1.objects.values("device").annotate(Count("device")).order_by()
于是就这么正常了,至于空的 order_by,相当于 ORDER BY NULL。