How to sort Django admin list column by a value from a related model18 Mar · by Tim Kamanin · 2 min read
Here's the real-life case.
On this blog, I have two models:
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)
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 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
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.