How to sort Django admin list column by a value from a related model
Mar 18, 2020 · by Tim KamaninHere'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:
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:
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:
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:
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:
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.