Django:在查询中添加“NULLS LAST”

116 投票
8 回答
42874 浏览
提问于 2025-04-17 17:21

我想用Postgresql的“NULLS LAST”选项来对一个模型进行排序。请问该怎么做呢?

我试过这样写:

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

但是我得到了这个错误:

“无法将关键字'NULLS LAST'解析为字段”

8 个回答

23

如果你想让这个过程变得透明,并且适用于所有列,你可以重新定义 SQL 的生成方式。为此,你需要创建一个自己的管理器,这样它就可以返回你自定义的查询集(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()
24

我找到的最接近的做法是分两步来处理。第一步是先按照有值的字段排序,然后再处理空值:

通过这个链接(它是通过这些django日志找到的):

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

注意:请留意关于extra()的警告,它可能在未来被弃用

205
from django.db.models import F  
MyModel.objects.all().order_by(F('price').desc(nulls_last=True))

这个功能是在Django 1.11版本中新增的。

https://docs.djangoproject.com/en/dev/releases/1.11/

新增了nulls_first和nulls_last这两个参数,可以用来控制空值的排序。

关于Django 3.1的参考资料:https://docs.djangoproject.com/en/3.1/ref/models/expressions/#using-f-to-sort-null-values

撰写回答