Django Order By Date, but have "None" at end?

I have a model of work orders, with a field for when the work order is required by. To get a list of work orders, with those that are required early, I do this:

wo = Work_Order.objects.order_by('dateWORequired')

This works nicely, but ONLY if there is actually a value in that field. If there is no required date, then the value is None. Then, the list of work orders has all the None's at the top, and then the remaining work orders in proper order.

How can I get the None's at the bottom?


Solution 1:

Django 1.11 added this as a native feature. It's a little convoluted. It is documented.

Ordered with only one field, ascending:

wo = Work_Order.objects.order_by(F('dateWORequired').asc(nulls_last=True))

Ordered using two fields, both descending:

wo = Work_Order.objects.order_by(F('dateWORequired').desc(nulls_last=True), F('anotherfield').desc(nulls_last=True))

Solution 2:

q = q.extra(select={
        'date_is_null': 'dateWORequired IS NULL',
    },
    order_by=['date_is_null','dateWORequired'],
)

You might need a - before the date_is_null in the order_by portion, but that's how you can control the behavior.

Solution 3:

This was 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
long_ago = datetime.datetime(year=1980, month=1, day=1)
Work_Order.objects.order_by('dateWORequired')
MyModel.objects.annotate(date_null=
    Coalesce('dateWORequired', Value(long_ago))).order_by('date_null')

Coalesce selects the first non-null value, so you create a value date_null to order by which is just dateWORequired but with null replaced by a date long ago.