Django ORM:使用聚合函数排序 — 无特殊处理

1 投票
1 回答
702 浏览
提问于 2025-04-15 21:12

我正在执行这个查询:

SomeObject.objects.annotate(something=Avg('something')).order_by(something).all()

我通常在我的模型中有一个聚合字段,用于配合Django信号保持同步,不过在这种情况下,性能不是问题,所以我想简单点,直接使用子查询。

然而,这种方法出现了一个意想不到的问题。只要聚合函数的结果是这样的:

[5.0, 4.0, 6.0 … (etc, just numbers)]

一切都运行得很好。但是如果结果中混入了一些 None,那么排序就会变成这样:

[None, 5.0, 4.0 …]

问题在于,None 的值比任何数字都要高,而它的值应该最多是0。

我使用的是PostgreSQL,没测试过其他数据库。我实际上也没有检查生成了什么查询等等。

我通过在内存中排序来解决了这个问题:

sorted(…, key=lambda _:_.avg_rating if _.avg_rating is not None else 0) 

所以我只是好奇,是否有办法仅通过Django的ORM来做到这一点?也许可以用 .where?或者其他什么方法?

1 个回答

2

你可以试试通过extra()添加一个has_something=1,0,然后在has_something和something这两个字段上进行排序。

with_avg = SomeObject.objects.annotate(avg=Avg('something'))
with_avg_and_has = with_avg.extra(select={'has_something': 'something is NULL'})
sorted_result = with_avg_and_has.order_by('-has_something', '-avg').all() 

这不是严格意义上的ORM(对象关系映射),但确实把排序的工作放回了数据库里。

撰写回答