django

Django prefetch_related's Prefetch, order_by?


I figured the following query:

 context['user_artists'] = Artist.objects.filter(users=current_user).all()

Coupled with the following usage in templates:

{% if user_artists %}
    ...
    {% for artist in user_artists %}
        ....
        <p class="small">last release: {{ artist.release_groups.last.title }}</p>
        <p class="small">date: {{ artist.release_groups.last.release_date }}</p>

Was hitting the database 3 times for every artist in the query. I know this can be brought down to 2 by simple saving .last in the template, but that still isn't fast enough.

I know I can use prefetch_related like so:

Artist.objects.filter(users=current_user).prefetch_related(`release_groups`).all()

And I also need eliminate he usage of .last since it implies another query. I can probably use the template engine's slice method to get the last element. But then I have to order the related relationship: meaning, I need release_group ordered by its own release_date before I access them in the template. This must not affect the order of the artist objects.

How can this be done?


Solution

  • You can use Prefetch:

    Artist.objects.prefetch_related(Prefetch('release_groups', queryset=ReleaseGroup.objects.order_by('release_date')))
    

    To avoid collision with the name, you can set to_attr to a different name:

    Artist.objects.prefetch_related(Prefetch('release_groups', queryset=ReleaseGroup.objects.order_by('release_date'), to_attr='rgs')).all()
    

    You can then access each cached release group in the template like so:

    artist.rgs.0.title