2011-03-08 72 views
37

我有一個django queryset排序問題。Django:按位置排序忽略NULL

我的模型包含一個名爲position(a PositiveSmallIntegerField)的字段,我想用它來對查詢結果進行排序。

我用order_by('position'),效果很好。

問題:我position場可爲空(null=True, blank=True),因爲我不wan't指定我的模型:(

每50000個實例的位置在某些情況下,有一個NULL「位置」 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?

非常感謝!

回答

52

您可以使用從django agrregation的註釋()這樣的伎倆:

items = Item.objects.all().annotate(null_position=Count('position')).order_by('-null_position', 'position') 
+0

是啊!它很好,非常感謝你mnik700! – user650108 2011-03-08 20:40:07

+1

這是一個非常整潔的伎倆,謝謝。 – 2012-07-11 15:21:35

+0

不知何故,這對我不起作用,也許是因爲我有一個訂單字段:position__name(這是position.name)。太糟糕了。 啊,等一下!我將null_position重命名爲null,看起來django不喜歡這樣!現在工作,謝謝! :) – gabn88 2015-09-14 17:27:16

1

QuerySet.extra()可用於將表達式注入查詢並由它們排序。

+0

這是現在已經過時。 – blueyed 2016-02-19 00:12:35

+0

'.extra()'不被棄用。但它可能在未來。 – 2016-06-28 07:29:10

9

使用額外的()爲伊格納西奧說,優化了很多最終查詢。在我aplication我節省了超過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}應該是類似{Item's app} _item之後的django的默認表名稱。

+0

是的,這個版本更快,謝謝! – 2013-11-16 14:37:53

+0

'extra()'已棄用,請參閱http://stackoverflow.com/a/35494930/15690。 – blueyed 2016-02-19 00:12:06

3

我發現,在巴勃羅的回答所需要的語法被更新到我的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') 
16

由於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') 
+6

感謝您的鏈接 - 這裏是我如何使用日期時間屬性上的'Coalesce':https://gist.github.com/yosemitebandit/ec3adc02d927375f13d0 – Matt 2015-06-23 00:19:16

+1

@ Matt的代碼應該添加到答案 - 它使它有用兩倍 – LostMyGlasses 2016-11-24 11:51:22

+0

我添加@Matts樣本到我的答案,所以千萬別忘記更新我們兩個:-) – shredding 2016-11-25 15:37:24