Mar 18, 2020 · by Tim Kamanin
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.
Hey, if you've found this useful, please share the post to help other folks find it: