Django:添加"NULLS LAST"进行查询

Gab*_*iMe 50 django

我想使用Postgresql的"NULLS LAST"选项对模型进行排序.怎么可能呢?

我试过类似的东西

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

但我明白了

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

kab*_*cey 87

from django.db.models import F  
MyModel.objects.all().order_by(F('price').desc(nulls_last=True))
Run Code Online (Sandbox Code Playgroud)

此功能已添加到Django 1.11.

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

在Expression.asc()和desc()中添加了nulls_first和nulls_last参数,以控制空值的排序.

  • 惊人.要使用它:`来自django.db.models import F``MyModel.objects.all().order_by(F('price').desc(nulls_last = True))` (7认同)
  • 有没有办法将其添加到模型的"Meta"类?或者我们是否也这样做,在`Meta`的`ordering`字段中使用`F`对象? (2认同)

Mar*_*ano 21

我发现的最近的事情是分两步完成.首先在填充字段上排序,然后在空值上排序:

通过这个要点(本身通过这些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
Run Code Online (Sandbox Code Playgroud)

祝好运.

  • 别客气.我不认为有一个完美的解决方案,或者至少有一个由Django直接考虑的解决方案,给出[这个谷歌组帖子](https://groups.google.com/d/msg/django-users/sxbpqmkRyCU/ qUYjX3niCOEJ)由Malcolm Treddinick提供. (2认同)

Tim*_*ych 21

如果您希望透明地在所有列上完成,则可以重新定义sql生成.为此,您需要拥有自己的Manager来返回自定义QuerySet以返回自定义Query以使用自定义编译器.我的代码看起来像那样(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()
Run Code Online (Sandbox Code Playgroud)

  • 这有点邪恶,但很棒. (5认同)

Mar*_*ark 11

当问到这个问题时,这可能不可用,但是自从Django 1.8以来我认为这是最好的解决方案:

from django.db.models import Coalesce, Value
MyModel.objects.all().annotate(price_null=
    Coalesce('price', Value(-100000000)).order_by('-price_null')
Run Code Online (Sandbox Code Playgroud)

Coalesce选择第一个非空值,因此您创建一个price_null按订单排序的值,只是价格但null-100000000(或+?)替换.


blu*_*yed 10

对于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)
Run Code Online (Sandbox Code Playgroud)

然后在您的模型上:

objects = NullsLastQuerySet.as_manager()
Run Code Online (Sandbox Code Playgroud)

这是基于Tim在/sf/answers/1195431121/中的回答.

向Django添加支持的票证已重新打开:https://code.djangoproject.com/ticket/13312.

  • @Duncan目前无法不付款,对不起。 (2认同)