Skip to content

Commit e53aea2

Browse files
ssb10carltongibson
andcommitted
Refs #19721 -- Moved ModelAdmin.list_filter docs into a separate file.
Co-authored-by: Carlton Gibson <[email protected]>
1 parent 1fe23bd commit e53aea2

File tree

2 files changed

+198
-152
lines changed

2 files changed

+198
-152
lines changed

docs/ref/contrib/admin/filters.txt

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
.. _modeladmin-list-filters:
2+
3+
===========================
4+
``ModelAdmin`` List Filters
5+
===========================
6+
7+
.. currentmodule:: django.contrib.admin
8+
9+
``ModelAdmin`` classes can define list filters that appear in the right sidebar
10+
of the change list page of the admin, as illustrated in the following
11+
screenshot:
12+
13+
.. image:: _images/list_filter.png
14+
15+
To activate per-field filtering, set :attr:`ModelAdmin.list_filter` to a list
16+
or tuple of elements, where each element is one of the following types:
17+
18+
- A field name.
19+
- A subclass of ``django.contrib.admin.SimpleListFilter``.
20+
- A 2-tuple containing a field name and a subclass of
21+
``django.contrib.admin.FieldListFilter``.
22+
23+
See the examples below for discussion of each of these options for defining
24+
``list_filter``.
25+
26+
Using a field name
27+
==================
28+
29+
The simplest option is to specify the required field names from your model.
30+
31+
Each specified field should be either a ``BooleanField``, ``CharField``,
32+
``DateField``, ``DateTimeField``, ``IntegerField``, ``ForeignKey`` or
33+
``ManyToManyField``, for example::
34+
35+
class PersonAdmin(admin.ModelAdmin):
36+
list_filter = ('is_staff', 'company')
37+
38+
Field names in ``list_filter`` can also span relations
39+
using the ``__`` lookup, for example::
40+
41+
class PersonAdmin(admin.UserAdmin):
42+
list_filter = ('company__name',)
43+
44+
Using a ``SimpleListFilter``
45+
============================
46+
47+
For custom filtering, you can define your own list filter by subclassing
48+
``django.contrib.admin.SimpleListFilter``. You need to provide the ``title``
49+
and ``parameter_name`` attributes, and override the ``lookups`` and
50+
``queryset`` methods, e.g.::
51+
52+
from datetime import date
53+
54+
from django.contrib import admin
55+
from django.utils.translation import gettext_lazy as _
56+
57+
class DecadeBornListFilter(admin.SimpleListFilter):
58+
# Human-readable title which will be displayed in the
59+
# right admin sidebar just above the filter options.
60+
title = _('decade born')
61+
62+
# Parameter for the filter that will be used in the URL query.
63+
parameter_name = 'decade'
64+
65+
def lookups(self, request, model_admin):
66+
"""
67+
Returns a list of tuples. The first element in each
68+
tuple is the coded value for the option that will
69+
appear in the URL query. The second element is the
70+
human-readable name for the option that will appear
71+
in the right sidebar.
72+
"""
73+
return (
74+
('80s', _('in the eighties')),
75+
('90s', _('in the nineties')),
76+
)
77+
78+
def queryset(self, request, queryset):
79+
"""
80+
Returns the filtered queryset based on the value
81+
provided in the query string and retrievable via
82+
`self.value()`.
83+
"""
84+
# Compare the requested value (either '80s' or '90s')
85+
# to decide how to filter the queryset.
86+
if self.value() == '80s':
87+
return queryset.filter(
88+
birthday__gte=date(1980, 1, 1),
89+
birthday__lte=date(1989, 12, 31),
90+
)
91+
if self.value() == '90s':
92+
return queryset.filter(
93+
birthday__gte=date(1990, 1, 1),
94+
birthday__lte=date(1999, 12, 31),
95+
)
96+
97+
class PersonAdmin(admin.ModelAdmin):
98+
list_filter = (DecadeBornListFilter,)
99+
100+
.. note::
101+
102+
As a convenience, the ``HttpRequest`` object is passed to the ``lookups``
103+
and ``queryset`` methods, for example::
104+
105+
class AuthDecadeBornListFilter(DecadeBornListFilter):
106+
107+
def lookups(self, request, model_admin):
108+
if request.user.is_superuser:
109+
return super().lookups(request, model_admin)
110+
111+
def queryset(self, request, queryset):
112+
if request.user.is_superuser:
113+
return super().queryset(request, queryset)
114+
115+
Also as a convenience, the ``ModelAdmin`` object is passed to the
116+
``lookups`` method, for example if you want to base the lookups on the
117+
available data::
118+
119+
class AdvancedDecadeBornListFilter(DecadeBornListFilter):
120+
121+
def lookups(self, request, model_admin):
122+
"""
123+
Only show the lookups if there actually is
124+
anyone born in the corresponding decades.
125+
"""
126+
qs = model_admin.get_queryset(request)
127+
if qs.filter(
128+
birthday__gte=date(1980, 1, 1),
129+
birthday__lte=date(1989, 12, 31),
130+
).exists():
131+
yield ('80s', _('in the eighties'))
132+
if qs.filter(
133+
birthday__gte=date(1990, 1, 1),
134+
birthday__lte=date(1999, 12, 31),
135+
).exists():
136+
yield ('90s', _('in the nineties'))
137+
138+
Using a field name and an explicit ``FieldListFilter``
139+
======================================================
140+
141+
Finally, if you wish to specify an explicit filter type to use with a field you
142+
may provide a ``list_filter`` item as a 2-tuple, where the first element is a
143+
field name and the second element is a class inheriting from
144+
``django.contrib.admin.FieldListFilter``, for example::
145+
146+
class PersonAdmin(admin.ModelAdmin):
147+
list_filter = (
148+
('is_staff', admin.BooleanFieldListFilter),
149+
)
150+
151+
Here the ``is_staff`` field will use the ``BooleanFieldListFilter``. Specifying
152+
only the field name, fields will automatically use the appropriate filter for
153+
most cases, but this format allows you to control the filter used.
154+
155+
The following examples show available filter classes that you need to opt-in
156+
to use.
157+
158+
You can limit the choices of a related model to the objects involved in
159+
that relation using ``RelatedOnlyFieldListFilter``::
160+
161+
class BookAdmin(admin.ModelAdmin):
162+
list_filter = (
163+
('author', admin.RelatedOnlyFieldListFilter),
164+
)
165+
166+
Assuming ``author`` is a ``ForeignKey`` to a ``User`` model, this will
167+
limit the ``list_filter`` choices to the users who have written a book,
168+
instead of listing all users.
169+
170+
You can filter empty values using ``EmptyFieldListFilter``, which can
171+
filter on both empty strings and nulls, depending on what the field
172+
allows to store::
173+
174+
class BookAdmin(admin.ModelAdmin):
175+
list_filter = (
176+
('title', admin.EmptyFieldListFilter),
177+
)
178+
179+
.. note::
180+
181+
The :class:`~django.contrib.contenttypes.fields.GenericForeignKey` field is
182+
not supported.
183+
184+
List filters typically appear only if the filter has more than one choice. A
185+
filter's ``has_output()`` method controls whether or not it appears.
186+
187+
It is possible to specify a custom template for rendering a list filter::
188+
189+
class FilterWithCustomTemplate(admin.SimpleListFilter):
190+
template = "custom_template.html"
191+
192+
See the default template provided by Django (``admin/filter.html``) for a
193+
concrete example.

docs/ref/contrib/admin/index.txt

Lines changed: 5 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Other topics
6666
:maxdepth: 1
6767

6868
actions
69+
filters
6970
admindocs
7071
javascript
7172

@@ -853,159 +854,11 @@ subclass::
853854
.. attribute:: ModelAdmin.list_filter
854855

855856
Set ``list_filter`` to activate filters in the right sidebar of the change
856-
list page of the admin, as illustrated in the following screenshot:
857-
858-
.. image:: _images/list_filter.png
859-
860-
``list_filter`` should be a list or tuple of elements, where each element
861-
should be of one of the following types:
862-
863-
* a field name, where the specified field should be either a
864-
``BooleanField``, ``CharField``, ``DateField``, ``DateTimeField``,
865-
``IntegerField``, ``ForeignKey`` or ``ManyToManyField``, for example::
866-
867-
class PersonAdmin(admin.ModelAdmin):
868-
list_filter = ('is_staff', 'company')
869-
870-
Field names in ``list_filter`` can also span relations
871-
using the ``__`` lookup, for example::
872-
873-
class PersonAdmin(admin.UserAdmin):
874-
list_filter = ('company__name',)
875-
876-
* a class inheriting from ``django.contrib.admin.SimpleListFilter``,
877-
which you need to provide the ``title`` and ``parameter_name``
878-
attributes to and override the ``lookups`` and ``queryset`` methods,
879-
e.g.::
880-
881-
from datetime import date
882-
883-
from django.contrib import admin
884-
from django.utils.translation import gettext_lazy as _
885-
886-
class DecadeBornListFilter(admin.SimpleListFilter):
887-
# Human-readable title which will be displayed in the
888-
# right admin sidebar just above the filter options.
889-
title = _('decade born')
890-
891-
# Parameter for the filter that will be used in the URL query.
892-
parameter_name = 'decade'
893-
894-
def lookups(self, request, model_admin):
895-
"""
896-
Returns a list of tuples. The first element in each
897-
tuple is the coded value for the option that will
898-
appear in the URL query. The second element is the
899-
human-readable name for the option that will appear
900-
in the right sidebar.
901-
"""
902-
return (
903-
('80s', _('in the eighties')),
904-
('90s', _('in the nineties')),
905-
)
906-
907-
def queryset(self, request, queryset):
908-
"""
909-
Returns the filtered queryset based on the value
910-
provided in the query string and retrievable via
911-
`self.value()`.
912-
"""
913-
# Compare the requested value (either '80s' or '90s')
914-
# to decide how to filter the queryset.
915-
if self.value() == '80s':
916-
return queryset.filter(birthday__gte=date(1980, 1, 1),
917-
birthday__lte=date(1989, 12, 31))
918-
if self.value() == '90s':
919-
return queryset.filter(birthday__gte=date(1990, 1, 1),
920-
birthday__lte=date(1999, 12, 31))
921-
922-
class PersonAdmin(admin.ModelAdmin):
923-
list_filter = (DecadeBornListFilter,)
924-
925-
.. note::
926-
927-
As a convenience, the ``HttpRequest`` object is passed to the
928-
``lookups`` and ``queryset`` methods, for example::
929-
930-
class AuthDecadeBornListFilter(DecadeBornListFilter):
931-
932-
def lookups(self, request, model_admin):
933-
if request.user.is_superuser:
934-
return super().lookups(request, model_admin)
935-
936-
def queryset(self, request, queryset):
937-
if request.user.is_superuser:
938-
return super().queryset(request, queryset)
939-
940-
Also as a convenience, the ``ModelAdmin`` object is passed to
941-
the ``lookups`` method, for example if you want to base the
942-
lookups on the available data::
943-
944-
class AdvancedDecadeBornListFilter(DecadeBornListFilter):
945-
946-
def lookups(self, request, model_admin):
947-
"""
948-
Only show the lookups if there actually is
949-
anyone born in the corresponding decades.
950-
"""
951-
qs = model_admin.get_queryset(request)
952-
if qs.filter(birthday__gte=date(1980, 1, 1),
953-
birthday__lte=date(1989, 12, 31)).exists():
954-
yield ('80s', _('in the eighties'))
955-
if qs.filter(birthday__gte=date(1990, 1, 1),
956-
birthday__lte=date(1999, 12, 31)).exists():
957-
yield ('90s', _('in the nineties'))
958-
959-
* a tuple, where the first element is a field name and the second
960-
element is a class inheriting from
961-
``django.contrib.admin.FieldListFilter``, for example::
962-
963-
class PersonAdmin(admin.ModelAdmin):
964-
list_filter = (
965-
('is_staff', admin.BooleanFieldListFilter),
966-
)
967-
968-
You can limit the choices of a related model to the objects involved in
969-
that relation using ``RelatedOnlyFieldListFilter``::
970-
971-
class BookAdmin(admin.ModelAdmin):
972-
list_filter = (
973-
('author', admin.RelatedOnlyFieldListFilter),
974-
)
975-
976-
Assuming ``author`` is a ``ForeignKey`` to a ``User`` model, this will
977-
limit the ``list_filter`` choices to the users who have written a book
978-
instead of listing all users.
979-
980-
You can filter empty values using ``EmptyFieldListFilter``, which can
981-
filter on both empty strings and nulls, depending on what the field
982-
allows to store::
983-
984-
class BookAdmin(admin.ModelAdmin):
985-
list_filter = (
986-
('title', admin.EmptyFieldListFilter),
987-
)
988-
989-
.. note::
990-
991-
The ``FieldListFilter`` API is considered internal and might be
992-
changed.
993-
994-
.. note::
995-
996-
The :class:`~django.contrib.contenttypes.fields.GenericForeignKey`
997-
field is not supported.
998-
999-
List filter's typically appear only if the filter has more than one choice.
1000-
A filter's ``has_output()`` method controls whether or not it appears.
1001-
1002-
It is possible to specify a custom template for rendering a list filter::
1003-
1004-
class FilterWithCustomTemplate(admin.SimpleListFilter):
1005-
template = "custom_template.html"
857+
list page of the admin.
1006858

1007-
See the default template provided by Django (``admin/filter.html``) for
1008-
a concrete example.
859+
At it's simplest ``list_filter`` takes a list or tuple of field names to
860+
activate filtering upon, but several more advanced options as available.
861+
See :ref:`modeladmin-list-filters` for the details.
1009862

1010863
.. attribute:: ModelAdmin.list_max_show_all
1011864

0 commit comments

Comments
 (0)