Django: split ModelAdmin.get_queryset() by view

Within Django’s popular admin site, you can override ModelAdmin.get_queryset() to customize the queryset used by the admin views. It’s often used for performance optimizations, such as adding a select_related() call to batch-fetch related objects:
from django.contrib import admin
from example.models import Book
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).select_related("author")
However, one thing this approach lacks is granularity—the queryset returned by get_queryset() is used for all admin views, such as the change list, change form, and any custom views that you might add. That can mean that adding an optimization in get_queryset() for one view can impose a performance cost on other views that don’t need it. For example, the above select_related() call might optimize showing author details shown on the change list view, but other pages that don’t show the author will still incur the cost of the join.
There isn’t an easy way to customize the queryset for individual views without overriding a lot of their code. However, the queryset() method is passed the current request object as context, which allows you to differentiate between views based on request.resolver_match. I think the most robust way to check the current admin view from there is with the __name__ attribute of the func:
from django.contrib import admin
from example.models import Book
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
def get_queryset(self, request):
queryset = super().get_queryset(request)
if request.resolver_match.func.__name__ == "changelist_view":
queryset = queryset.select_related("author")
return queryset
request.resolver_match.func is the current view function, which will be a method of the current ModelAdmin instance, wrapped with the AdminSite.admin_view decorator. Its __name__ attribute gives the name of the view function, which you can use to differentiate between views. For the built-in admin views, it will be one of the following:
changelist_viewchange_viewdelete_viewhistory_view
(add_view is not included here as it does not call get_queryset().)
😸😸😸 Check out my new book on using GitHub effectively, Boost Your GitHub DX! 😸😸😸
One summary email a week, no spam, I pinky promise.
Related posts:
- Django: what’s new in 5.2
- Django: iterate through all registered URL patterns
- Django: hide the development server warning
Tags: django