2013-02-27 78 views
27

我想使用Postgresql的「NULLS LAST」選項對模型進行排序。 怎麼辦?Django:將「NULLS LAST」添加到查詢

我想是這樣

MyModel.objects.all().extra(order_by=('-price', 'NULLS LAST'))

,但我得到

「無法解析關鍵字‘空值LAST’到現場」

我發現

回答

15

最近的事情是做什麼的兩腳步。所填充的字段,然後在空第一順序:

通過this要點(本身通過這些django logs):

all_projects = Project.objects.select_related().filter(
    company=company).order_by('-date_due') 

q = all_projects.extra(select={'date_due_null': 'date_due is null'}) 
q = q.extra(order_by=['date_due_null']) 
print q.query 

好運。

+0

感謝。這確實不是完美的解決方案,但有點作品 – GabiMe 2013-02-28 20:44:08

+2

不客氣。我認爲沒有一個完美的解決方案,或者至少有一個由Django直接設想的[谷歌組織發佈](https://groups.google.com/d/msg/django-users/sxbpqmkRyCU/ qUYjX3niCOEJ)由馬爾科姆Treddinick。 – Mariano 2013-02-28 22:07:00

20

如果你希望它是透明的完成和所有列,您可以重新定義SQL生成。爲此,您需要讓自己的Manager返回您的自定義QuerySet,以返回您的自定義Query以使用自定義Compiler。我的代碼看起來像這樣(Django的1.5):

from django.db import models, connections 

class NullsLastQuery(models.sql.query.Query): 
    """ 
    Query that uses custom compiler, 
    to utilize PostgreSQL feature of setting position of NULL records 
    """ 
    def get_compiler(self, using=None, connection=None): 
     if using is None and connection is None: 
      raise ValueError("Need either using or connection") 
     if using: 
      connection = connections[using] 

     # defining that class elsewhere results in import errors 
     from django.db.models.sql.compiler import SQLCompiler 
     class NullsLastSQLCompiler(SQLCompiler): 
      def get_ordering(self): 
       result, group_by = super(NullsLastSQLCompiler, self 
        ).get_ordering() 
       if self.connection.vendor == 'postgresql' and result: 
        result = [line + " NULLS LAST" for line in result] 
       return result, group_by 

     return NullsLastSQLCompiler(self, connection, using) 

class NullsLastQuerySet(models.query.QuerySet): 
    def __init__(self, model=None, query=None, using=None): 
     super(NullsLastQuerySet, self).__init__(model, query, using) 
     self.query = query or NullsLastQuery(self.model) 

class NullsLastManager(models.Manager): 
    def get_query_set(self): 
     return NullsLastQuerySet(self.model, using=self._db) 

class YourModel(models.Model): 
    objects = NullsLastManager() 
+5

這有點邪惡,但很精彩。 – 2013-09-23 15:12:42

9

Django的1.9(也可能是1.8),您可以使用此:

from django.db import connections, models 
from django.db.models.sql.compiler import SQLCompiler 


class NullsLastSQLCompiler(SQLCompiler): 
    def get_order_by(self): 
     result = super().get_order_by() 
     if result and self.connection.vendor == 'postgresql': 
      return [(expr, (sql + ' NULLS LAST', params, is_ref)) 
        for (expr, (sql, params, is_ref)) in result] 
     return result 


class NullsLastQuery(models.sql.query.Query): 
    """Use a custom compiler to inject 'NULLS LAST' (for PostgreSQL).""" 

    def get_compiler(self, using=None, connection=None): 
     if using is None and connection is None: 
      raise ValueError("Need either using or connection") 
     if using: 
      connection = connections[using] 
     return NullsLastSQLCompiler(self, connection, using) 


class NullsLastQuerySet(models.QuerySet): 
    def __init__(self, model=None, query=None, using=None, hints=None): 
     super().__init__(model, query, using, hints) 
     self.query = query or NullsLastQuery(self.model) 

,然後在你的模型(S):

objects = NullsLastQuerySet.as_manager() 

這是基於從蒂姆https://stackoverflow.com/a/17077587/15690答案。

爲它添加支持Django的車票已經重開:https://code.djangoproject.com/ticket/13312

+0

你救了我的一天。 – levi 2016-04-06 18:40:52

+0

關於如何在最後放置空字符串的功能,您是否有任何想法? – Duncan 2016-06-01 22:58:59

+1

@Duncan此時此刻沒有被付出,對不起..;) – blueyed 2016-06-03 21:39:02

7

這可能是不可用的時候有人問,但因爲Django的1.8,我認爲這是最好的解決辦法:

from django.db.models.functions import Coalesce, Value 
MyModel.objects.all().annotate(price_null= 
    Coalesce('price', Value(-100000000)).order_by('-price_null') 

Coalesce選擇的第一個非空值,讓你創造一個價值price_null到順序是隻是價格,但用null取代-100000000(或+?)。

+0

這實際上是一個很好的破解,因爲它不需要創建NULLS LAST索引和類似的東西來加速查詢 – valignatev 2016-10-27 12:37:17

+1

@valentjedi這不是需要一個表達式索引'Coalesce('price',Value(-100000000)'?這與創建一個NULLS LAST索引是一樣的。這裏http://dba.stackexchange.com/a/99009有人聲明相同我的意思是 – jperelli 2016-12-09 14:56:13

+0

@ jperelli實際上你是對的,但在我的情況下,這種破解比添加NULLS LAST快,所以它讓我滿意。 – valignatev 2016-12-09 19:20:04