2013-02-27 115 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