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
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:
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)
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 +
?).