How to sort Django admin list column by a value from a related model

Mar 18, 2020 ยท Updated: Jul 12, 2021 ยท by Tim Kamanin

Here's a real-life case.

On this blog, I have two models: Post and PostStats. As you may have already guessed, the first is for posts and the second is for statistics on each post, such as the number of views.

Here is a simplified version of both models:
class Post(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()

class PostStats(models.Model):
    post = models.ForeignKey(
        "Post", on_delete=models.CASCADE, related_name="post_stats"
    view_count = models.PositiveIntegerField(default=0)

I have the following ModelAdmin class for the Post model:
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "live"]

Now I want to add the 'view_count' column from the related PostStats model to PostAdmin and do sorting on that column.

My first intention was to declare view_count as a query lookup:
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "live", "post_stats__view_count"]

Note, I added post_stats__view_count where post_stats is the name of the Post -> PostStats relationship, and view_count is the name of the field I want to sort by. Unfortunately, ModelAdmin wasn't happy about it and threw me an error.

Then, let's try something else. So you can add a synthetic column to ModelAdmin as follows:
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "live", "get_view_count"]

    def get_view_count(self, obj):
        return obj.tutorialstats.view_count

This gives us a get_view_count column with the desired value, but there are still two issues: you can't click to sort the column, and its name is ugly.

After digging around in the innards of ModelAdmin a bit, I finally found a solution! It turns out that get_view_count method can have two properties (actually it can have more, but we are only interested in the following two):

  1. admin_order_field - is the name of the field by which you want to sort the custom column. The cool thing is that admin_order_field supports query lookups like stats__view_count, bingo!;
  2. short_description - allows you to change the name of the column.

Now we have everything we need to solve our problem, and the complete solution looks like this:
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "live", "get_view_count"]

    def get_view_count(self, obj):
        return obj.tutorialstats.view_count

    get_view_count.admin_order_field = "post_stats__view_count"
    get_view_count.short_description = "View count"

Click on the "View count " column, and you will get the results of the admin list, sorted by the value of the column.

Hey, if you've found this useful, please share the post to help other folks find it:

There's even more:

Subscribe for updates

  • via Twitter: @timonweb
  • old school RSS:
  • or evergreen email โ†“ โ†“ โ†“