Zemogle
Zemogle

Reputation: 604

How to sort a queryset of different models in Wagtail/Django?

I have a Wagtail site and I'm making a listing page for a few different Page types/Content types. I filter by a snippet field first:

def highlights_list(category='Highlight'):
    tag = ContentCategory.objects.get(name=category)
    highlights = Page.objects.filter(
        Q(event__categories=tag)|
        Q(newspage__categories=tag)|
        Q(contentpage__categories=tag)).live()
    return highlights.order_by('-last_published_at')

In Wagtail all content types inherit from the base Page class which makes creating a queryset with all the content types I want really easy. But I can't work out how to sort nicely.

Sorting by last_published_at is fine for NewsPage and ContentPage but not for Event where I'd like to sort by the DateTimeField for the event.

I thought about making a @property on all the models called sort_date which uses the datetime field specific to each model that I'd like to sort on, but that just doesn't work.

Any suggestions are very welcome!

Upvotes: 0

Views: 1658

Answers (1)

acarasimon96
acarasimon96

Reputation: 303

I can think of two solutions for this, but only one seems to be easier to do. I changed your function above to this:

def highlights_list(category='Highlight'):
    tag = ContentCategory.objects.get(name=category)
    highlights = Page.objects.filter(
        Q(event__categories=tag)|
        Q(newspage__categories=tag)|
        Q(contentpage__categories=tag)
    ).live().specific()

    return sorted(
        highlights,
        key=lambda p: getattr(p, 'date') or getattr(p, 'last_published_at'),
        reverse=True
    )

What this does is it takes all the pages from highlights and sorts them based on the value in date or last_published_at as if they're the same field. Because only the Event page has a date field, last_published_at is used as the fallback, a field all live pages have in common. If you leave out specific() in your queryset like in your question, it throws an AttributeError saying 'Page' object has no attribute 'date'.

As a bonus, the sorting part doesn't use any database queries since it is done in Python. Note that this returns a list based on the original QuerySet.

The second solution involves database transactions and Wagtail hooks and has more code, but that is something I don't want to share as I haven't thought of how to write that out.

Upvotes: 1

Related Questions