DjangoDjango

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

Mar 18, 2020 ยท by Tim Kamanin

Here's the real-life case.

On this blog, I have two models: Post and PostStats. The former is, as you might have guessed, for posts, while the latter is for per post statistics like a view count.

Here's a simplified version of both models:

python
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)

For the Post model I have the following ModelAdmin class:

python
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'live']

Now I want to add a 'view_count' column from a related PostStats model to PostAdmin and be able to do a sort by this column.

My first intention was to declare view_count as a query lookup:

python
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'live', 'post_stats__view_count']

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

Let's try something else then. Okay, you can add a synthetic column to ModelAdmin like this:

python
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'live', 'get_view_count']

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

That gives us 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 inside ModelAdmin internals a bit, I've finally found the solution! It turns out that get_view_count method may have two properties (actually it can have more, we're just interested in the following two):
admin_order_field - is a field name you want the custom column to sort by. The cool thing is that admin_order_field supports query lookups like stats__view_count, bingo!;
short_description - allows you to change the column name.

Now we have everything in place to solve our problem, and the complete solution looks like:

python
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 should get the admin list results sorted by the column value.

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 โ†“ โ†“ โ†“