Django: Adding “NULLS LAST” to query

前端 未结 8 2069
时光说笑
时光说笑 2020-11-27 14:25

I would like to sort a model by using Postgresql\'s \"NULLS LAST\" option. How could it be done?

I tried something like

MyModel.objects.all().extra(ord

相关标签:
8条回答
  • 2020-11-27 14:53

    There is an another way to add managed nulls functionality to Django < v1.11 with Django v1.11 style:

    from my_project.utils.django import F
    MyModel.objects.all().order_by(F('price').desc(nulls_last=True))
    # or
    MyModel.objects.all().order_by(F('price').desc().nullslast())
    

    Cons:

    1. Easy migration to Django 1.11
    2. We don't get deep into query compiler internals

    To do so we need to override django.db.models.F and django.db.models.expressions.OrderBy classes:

    from django.db.models import F as DjangoF
    from django.db.models.expression import OrderBy as DjangoOrderBy
    
    
    class OrderBy(DjangoOrderBy):
        def __init__(self, expression, descending=False, nulls_last=None):
            super(OrderBy, self).__init__(expression, descending)
            self.nulls_last = nulls_last
        ...
    
        def as_sql(self, compiler, connection, template=None, **extra_context):
            ...
            ordering_value = 'DESC' if self.descending else 'ASC'
            if self.nulls_last is not None:
                nulls_value = 'LAST' if self.nulls_last else 'FIRST'
                ordering_value += ' NULLS ' + nulls_value
    
            placeholders = {
                'expression': expression_sql,
                'ordering': ordering_value,
            }
            ...
    
        def nullslast(self):
            self.nulls_last = True
    
        def nullsfirst(self):
            self.nulls_last = False
    
    
    class F(DjangoF):
        ...
    
        def asc(self, nulls_last=None):
            return OrderBy(self, nulls_last=nulls_last)
    
        def desc(self, nulls_last=None):
            return OrderBy(self, descending=True, nulls_last=nulls_last)
    
    0 讨论(0)
  • 2020-11-27 14:56

    This was probably not available when the question was asked, but since Django 1.8 I think this is the best solution:

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

    Coalesce selects the first non-null value, so you create a value price_null to order by which is just price but with null replaced by -100000000 (or +?).

    0 讨论(0)
提交回复
热议问题