Django: How to order a queryset and move some items to end of the queryset

Viewed 487

I have a queryset in my Django application:

databytes_all = DataByte.objects

Each item in the databytes_all queryset has many attributes but one of them is publish_date.

I'd like to order the queryset by publish_date, however if publish_date is None, I'd like the item to be at the end of the queryset.

This is what I'm trying but it's not working:

databytes_all = DataByte.objects

Make a queryset: filter out all of the publish dates that are None

no_date = databytes_all.filter(publish_date=None)

Make anohther queryset: exclude all of the items where publish date is none, then order the remaining items by publish date

databytes_with_date = databytes_all.order_by('-publish_date').exclude(publish_date=None)

Now combine the two querysets (however this doesnt work- the items with no publish date are first in the list when I want them to be last)

databytes = databytes_with_date | no_date 
2 Answers

You do not need to filter, you can specify that the NULL should be last with:

from django.db.models import F

databytes_all.order_by(F('publish_date').desc(nulls_last=True))

We here thus make use of an F object [Django-doc] where we call the .desc(…) method [Django-doc]. This method has a nulls_last=… parameter that we can use to specify that items with NULL should be selected as the last records.

This will sort with:

SELECT …
FROM …
ORDER BY publish_date IS NULL, publish_date DESC

since False/0 is considered less than True/1, it will thus move the items with publish_date to the bottom of the records you retrieve.

In Django, you can combine a lot of QuerySet using chain try this:

from itertools import chain

...

combined_results = list(chain(databytes_with_date, no_date))

Related