我对django queryset排序有疑问。
我的模型包含一个名为position
的字段( PositiveSmallIntegerField ),我想用它来订购查询结果。
我使用order_by('position')
,效果很好。
问题:我的position
字段可以为空(null=True, blank=True
),因为我不想为我的模型的每50000个实例指定一个位置:(
当某些实例具有NULL“position”时,order_by
会将它们返回到列表顶部:我希望它们在最后...
在RAW SQL中,我曾经写过像“IF(position IS NULL or position='', 1, 0)
”这样的东西(参见http://www.shawnolson.net/a/730/mysql-sort-order-with-null.html):是否可以使用Django获得相同的结果,而无需编写原始SQL?
非常感谢!
答案 0 :(得分:57)
您可以使用django agrregation中的annotate()来完成这一操作:
items = Item.objects.all().annotate(null_position=Count('position')).order_by('-null_position', 'position')
答案 1 :(得分:19)
从Django 1.8开始,您可以使用Coalesce()
将NULL
转换为0
。
样品:
import datetime
from django.db.models.functions import Coalesce, Value
from app import models
# Coalesce works by taking the first non-null value. So we give it
# a date far before any non-null values of last_active. Then it will
# naturally sort behind instances of Box with a non-null last_active value.
the_past = datetime.datetime.now() - datetime.timedelta(days=10*365)
boxes = models.Box.objects.all().annotate(
new_last_active=Coalesce(
'last_active', Value(the_past)
)
).order_by('-new_last_active')
答案 2 :(得分:9)
使用extra(),因为Ignacio说优化了很多结束查询。在我的应用程序中,我使用extra()而不是annotate()
在数据库处理中保存了超过500毫秒(这对查询来说很多)以下是您的情况:
items = Item.objects.all().extra(
'select': {
'null_position': 'CASE WHEN {tablename}.position IS NULL THEN 0 ELSE 1 END'
}
).order_by('-null_position', 'position')
{tablename}应该是django默认表名后的 {Item's app} _item 。
答案 3 :(得分:5)
很遗憾在SO上有很多这样的问题都没有标记为重复。有关Django 1.11及更新版本的本机解决方案,请参阅(例如)this answer。这是一个简短的摘录:
在Expression.asc()和desc()中添加了nulls_first和nulls_last参数,以控制空值的排序。
示例用法(从评论到答案):
from django.db.models import F
MyModel.objects.all().order_by(F('price').desc(nulls_last=True))
归功于最初的答案作者和评论者。
答案 4 :(得分:3)
我发现Pablo的答案中的语法需要在我的1.7.1安装中更新到以下内容:
items = Item.objects.all().extra(select={'null_position': 'CASE WHEN {name of Item's table}.position IS NULL THEN 0 ELSE 1 END'}).order_by('-null_position', 'position')
答案 5 :(得分:1)
QuerySet.extra()
可用于将表达式注入查询并按顺序排序。