django admin site search
1.1.0
A global/site search modal for the Django admin.
model_char_fields
(default): All CharField
(and subclass) values, with __icontains
.admin_search_fields
: Invoke each ModelAdmin's
get_search_results(...) method.pip install django-admin-site-search
.admin_site_search
to your INSTALLED_APPS
setting.AdminSiteSearchView
to your AdminSite:from django.contrib import admin
from admin_site_search.views import AdminSiteSearchView
class MyAdminSite(AdminSiteSearchView, admin.AdminSite):
...
admin/base_site.html
in your templates/
directory.
templates/
directory is inside of an app, then that app must appear in INSTALLED_APPS
before your custom admin app.admin_site_search
templates:{% extends "admin/base_site.html" %}
{% block extrahead %}
{% include 'admin_site_search/head.html' %}
{{ block.super }}
{% endblock %}
{% block footer %}
{{ block.super }}
{% include 'admin_site_search/modal.html' %}
{% endblock %}
{% block usertools %}
{% include 'admin_site_search/button.html' %}
{{ block.super }}
{% endblock %}
admin_site_search/head.html
loads Alpine JS.
/static/
, to avoid external dependencies.modal.html
and button.html
are not strict, though the former would ideally be in a top-level
position.
{% block header %}
- this is preferable to footer
.class MyAdminSite(AdminSiteSearchView, admin.AdminSite):
# Sets the last part of the search route (`<admin_path>/search/`).
site_search_path: str = "search/"
# Set the search method/behaviour.
site_search_method: Literal["model_char_fields", "admin_search_fields"] = "model_char_fields"
def match_app(
self, request, query: str, name: str
) -> bool:
"""DEFAULT: case-insensitive match the app name"""
def match_model(
self, request, query: str, name: str, object_name: str, fields: List[Field]
) -> bool:
"""DEFAULT: case-insensitive match the model and field attributes"""
def match_objects(
self, request, query: str, model_class: Model, model_fields: List[Field]
) -> QuerySet:
"""DEFAULT: Returns the QuerySet after performing an OR filter across all Char fields in the model."""
def filter_field(
self, request, query: str, field: Field
) -> Optional[Q]:
"""DEFAULT: Returns a Q 'icontains' filter for Char fields, otherwise None
Note: this method is only invoked if model_char_fields is the site_search_method."""
def get_model_queryset(
self, request, model_class: Model, model_admin: Optional[ModelAdmin]
) -> QuerySet:
"""DEFAULT: Returns the model class' .objects.all() queryset."""
def get_model_class(
self, request, app_label: str, model_dict: dict
) -> Optional[Model]:
"""DEFAULT: Retrieve the model class from the dict created by admin.AdminSite"""
Add TextField
results to search.
from django.contrib import admin
from django.db.models import Q, Field, TextField
from admin_site_search.views import AdminSiteSearchView
class MyAdminSite(AdminSiteSearchView, admin.AdminSite):
site_search_method: "model_char_fields"
def filter_field(self, request, query: str, field: Field) -> Optional[Q]:
"""Extends super() to add TextField support to site search"""
if isinstance(field, TextField):
return Q(**{f"{field.name}__icontains": query})
return super().filter_field(query, field)
Note that this isn't done by default for performance reasons: __icontains
on a
large number of text entries is suboptimal.