Descriptive Django Admin Search
At my job we have a growing number of people solely focused on operations (they outnumber us devs!). One of them pointed
out to me that she never knew when she could search for a tenant’s name in the search bar versus using the dropdown
(list_filter
). It didn’t take me long to realize we have a critical usability issue in our application. We can support
all this search functionality in the admin, but if a person doesn’t know what it can do they can’t use it.
Problem: Staff doesn’t know what the search does.
Solution: List the fields that are being searched.
Composition to the rescue!1
class DescriptiveSearchMixin:
"""
Add the verbose model and field names to the search help text.
This will include the model and field name for each search field.
If a relationship is more than one related model, it will only display
the last model of the relationship.
If a ModelAdmin class has a value for search_help_text, the descriptive
search text will not be added.
"""
def get_search_fields(self, request):
"""
Override get_search_fields to dynamically set search_help_text
This is potentially problematic if you're doing something more complicated
with the admin already. In my case the search functionality is vanilla so this
works.
"""
search_fields = super().get_search_fields(request)
if search_fields and self.search_help_text is None:
field_strings = []
for search_field in search_fields:
current_model_meta = self.model._meta
if "__" in search_field:
field_path = search_field.split("__")
# Follow the relationships down to the last path.
for path in field_path:
field = current_model_meta.get_field(path)
if not getattr(field, "related_model"):
break
current_model_meta = field.related_model._meta
else:
field = current_model_meta.get_field(search_field)
# Include the human-readable version of the model and field name.
field_strings.append(
f"{current_model_meta.verbose_name.title()}'s {field.verbose_name.title()}"
)
self.search_help_text = f'Search by: {", ".join(field_strings)}'
return search_fields
To use this, simply inherit from the mixin class:
from my_wonderful_app.models import Plan
class PlanAdmin(DescriptiveSearchMixin, admin.ModelAdmin):
search_fields = ["name", "creator__name", "creator__email", "organization__name"]
Now when you browse to /admin/plan/
you’ll see:
Search by: Plan’s Name, User’s Name, User’s Email, Organization’s Name
At the end of the day it doesn’t matter what you build if nobody knows about it. Be better than me at informing your users what’s possible!
-
I can’t pass up a opportunity to hop on the Django zeitgeist (See Matthias’ post and Carlton’s post on composition) ↩