Refs #19721 -- Moved ModelAdmin.list_filter docs into a separate file.
Co-authored-by: Carlton Gibson <carlton.gibson@noumenal.es>
This commit is contained in:
parent
1fe23bdd29
commit
e53aea2e23
|
@ -0,0 +1,193 @@
|
|||
.. _modeladmin-list-filters:
|
||||
|
||||
===========================
|
||||
``ModelAdmin`` List Filters
|
||||
===========================
|
||||
|
||||
.. currentmodule:: django.contrib.admin
|
||||
|
||||
``ModelAdmin`` classes can define list filters that appear in the right sidebar
|
||||
of the change list page of the admin, as illustrated in the following
|
||||
screenshot:
|
||||
|
||||
.. image:: _images/list_filter.png
|
||||
|
||||
To activate per-field filtering, set :attr:`ModelAdmin.list_filter` to a list
|
||||
or tuple of elements, where each element is one of the following types:
|
||||
|
||||
- A field name.
|
||||
- A subclass of ``django.contrib.admin.SimpleListFilter``.
|
||||
- A 2-tuple containing a field name and a subclass of
|
||||
``django.contrib.admin.FieldListFilter``.
|
||||
|
||||
See the examples below for discussion of each of these options for defining
|
||||
``list_filter``.
|
||||
|
||||
Using a field name
|
||||
==================
|
||||
|
||||
The simplest option is to specify the required field names from your model.
|
||||
|
||||
Each specified field should be either a ``BooleanField``, ``CharField``,
|
||||
``DateField``, ``DateTimeField``, ``IntegerField``, ``ForeignKey`` or
|
||||
``ManyToManyField``, for example::
|
||||
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
list_filter = ('is_staff', 'company')
|
||||
|
||||
Field names in ``list_filter`` can also span relations
|
||||
using the ``__`` lookup, for example::
|
||||
|
||||
class PersonAdmin(admin.UserAdmin):
|
||||
list_filter = ('company__name',)
|
||||
|
||||
Using a ``SimpleListFilter``
|
||||
============================
|
||||
|
||||
For custom filtering, you can define your own list filter by subclassing
|
||||
``django.contrib.admin.SimpleListFilter``. You need to provide the ``title``
|
||||
and ``parameter_name`` attributes, and override the ``lookups`` and
|
||||
``queryset`` methods, e.g.::
|
||||
|
||||
from datetime import date
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class DecadeBornListFilter(admin.SimpleListFilter):
|
||||
# Human-readable title which will be displayed in the
|
||||
# right admin sidebar just above the filter options.
|
||||
title = _('decade born')
|
||||
|
||||
# Parameter for the filter that will be used in the URL query.
|
||||
parameter_name = 'decade'
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
"""
|
||||
Returns a list of tuples. The first element in each
|
||||
tuple is the coded value for the option that will
|
||||
appear in the URL query. The second element is the
|
||||
human-readable name for the option that will appear
|
||||
in the right sidebar.
|
||||
"""
|
||||
return (
|
||||
('80s', _('in the eighties')),
|
||||
('90s', _('in the nineties')),
|
||||
)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
"""
|
||||
Returns the filtered queryset based on the value
|
||||
provided in the query string and retrievable via
|
||||
`self.value()`.
|
||||
"""
|
||||
# Compare the requested value (either '80s' or '90s')
|
||||
# to decide how to filter the queryset.
|
||||
if self.value() == '80s':
|
||||
return queryset.filter(
|
||||
birthday__gte=date(1980, 1, 1),
|
||||
birthday__lte=date(1989, 12, 31),
|
||||
)
|
||||
if self.value() == '90s':
|
||||
return queryset.filter(
|
||||
birthday__gte=date(1990, 1, 1),
|
||||
birthday__lte=date(1999, 12, 31),
|
||||
)
|
||||
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
list_filter = (DecadeBornListFilter,)
|
||||
|
||||
.. note::
|
||||
|
||||
As a convenience, the ``HttpRequest`` object is passed to the ``lookups``
|
||||
and ``queryset`` methods, for example::
|
||||
|
||||
class AuthDecadeBornListFilter(DecadeBornListFilter):
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
if request.user.is_superuser:
|
||||
return super().lookups(request, model_admin)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
if request.user.is_superuser:
|
||||
return super().queryset(request, queryset)
|
||||
|
||||
Also as a convenience, the ``ModelAdmin`` object is passed to the
|
||||
``lookups`` method, for example if you want to base the lookups on the
|
||||
available data::
|
||||
|
||||
class AdvancedDecadeBornListFilter(DecadeBornListFilter):
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
"""
|
||||
Only show the lookups if there actually is
|
||||
anyone born in the corresponding decades.
|
||||
"""
|
||||
qs = model_admin.get_queryset(request)
|
||||
if qs.filter(
|
||||
birthday__gte=date(1980, 1, 1),
|
||||
birthday__lte=date(1989, 12, 31),
|
||||
).exists():
|
||||
yield ('80s', _('in the eighties'))
|
||||
if qs.filter(
|
||||
birthday__gte=date(1990, 1, 1),
|
||||
birthday__lte=date(1999, 12, 31),
|
||||
).exists():
|
||||
yield ('90s', _('in the nineties'))
|
||||
|
||||
Using a field name and an explicit ``FieldListFilter``
|
||||
======================================================
|
||||
|
||||
Finally, if you wish to specify an explicit filter type to use with a field you
|
||||
may provide a ``list_filter`` item as a 2-tuple, where the first element is a
|
||||
field name and the second element is a class inheriting from
|
||||
``django.contrib.admin.FieldListFilter``, for example::
|
||||
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
list_filter = (
|
||||
('is_staff', admin.BooleanFieldListFilter),
|
||||
)
|
||||
|
||||
Here the ``is_staff`` field will use the ``BooleanFieldListFilter``. Specifying
|
||||
only the field name, fields will automatically use the appropriate filter for
|
||||
most cases, but this format allows you to control the filter used.
|
||||
|
||||
The following examples show available filter classes that you need to opt-in
|
||||
to use.
|
||||
|
||||
You can limit the choices of a related model to the objects involved in
|
||||
that relation using ``RelatedOnlyFieldListFilter``::
|
||||
|
||||
class BookAdmin(admin.ModelAdmin):
|
||||
list_filter = (
|
||||
('author', admin.RelatedOnlyFieldListFilter),
|
||||
)
|
||||
|
||||
Assuming ``author`` is a ``ForeignKey`` to a ``User`` model, this will
|
||||
limit the ``list_filter`` choices to the users who have written a book,
|
||||
instead of listing all users.
|
||||
|
||||
You can filter empty values using ``EmptyFieldListFilter``, which can
|
||||
filter on both empty strings and nulls, depending on what the field
|
||||
allows to store::
|
||||
|
||||
class BookAdmin(admin.ModelAdmin):
|
||||
list_filter = (
|
||||
('title', admin.EmptyFieldListFilter),
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
The :class:`~django.contrib.contenttypes.fields.GenericForeignKey` field is
|
||||
not supported.
|
||||
|
||||
List filters typically appear only if the filter has more than one choice. A
|
||||
filter's ``has_output()`` method controls whether or not it appears.
|
||||
|
||||
It is possible to specify a custom template for rendering a list filter::
|
||||
|
||||
class FilterWithCustomTemplate(admin.SimpleListFilter):
|
||||
template = "custom_template.html"
|
||||
|
||||
See the default template provided by Django (``admin/filter.html``) for a
|
||||
concrete example.
|
|
@ -66,6 +66,7 @@ Other topics
|
|||
:maxdepth: 1
|
||||
|
||||
actions
|
||||
filters
|
||||
admindocs
|
||||
javascript
|
||||
|
||||
|
@ -853,159 +854,11 @@ subclass::
|
|||
.. attribute:: ModelAdmin.list_filter
|
||||
|
||||
Set ``list_filter`` to activate filters in the right sidebar of the change
|
||||
list page of the admin, as illustrated in the following screenshot:
|
||||
list page of the admin.
|
||||
|
||||
.. image:: _images/list_filter.png
|
||||
|
||||
``list_filter`` should be a list or tuple of elements, where each element
|
||||
should be of one of the following types:
|
||||
|
||||
* a field name, where the specified field should be either a
|
||||
``BooleanField``, ``CharField``, ``DateField``, ``DateTimeField``,
|
||||
``IntegerField``, ``ForeignKey`` or ``ManyToManyField``, for example::
|
||||
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
list_filter = ('is_staff', 'company')
|
||||
|
||||
Field names in ``list_filter`` can also span relations
|
||||
using the ``__`` lookup, for example::
|
||||
|
||||
class PersonAdmin(admin.UserAdmin):
|
||||
list_filter = ('company__name',)
|
||||
|
||||
* a class inheriting from ``django.contrib.admin.SimpleListFilter``,
|
||||
which you need to provide the ``title`` and ``parameter_name``
|
||||
attributes to and override the ``lookups`` and ``queryset`` methods,
|
||||
e.g.::
|
||||
|
||||
from datetime import date
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class DecadeBornListFilter(admin.SimpleListFilter):
|
||||
# Human-readable title which will be displayed in the
|
||||
# right admin sidebar just above the filter options.
|
||||
title = _('decade born')
|
||||
|
||||
# Parameter for the filter that will be used in the URL query.
|
||||
parameter_name = 'decade'
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
"""
|
||||
Returns a list of tuples. The first element in each
|
||||
tuple is the coded value for the option that will
|
||||
appear in the URL query. The second element is the
|
||||
human-readable name for the option that will appear
|
||||
in the right sidebar.
|
||||
"""
|
||||
return (
|
||||
('80s', _('in the eighties')),
|
||||
('90s', _('in the nineties')),
|
||||
)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
"""
|
||||
Returns the filtered queryset based on the value
|
||||
provided in the query string and retrievable via
|
||||
`self.value()`.
|
||||
"""
|
||||
# Compare the requested value (either '80s' or '90s')
|
||||
# to decide how to filter the queryset.
|
||||
if self.value() == '80s':
|
||||
return queryset.filter(birthday__gte=date(1980, 1, 1),
|
||||
birthday__lte=date(1989, 12, 31))
|
||||
if self.value() == '90s':
|
||||
return queryset.filter(birthday__gte=date(1990, 1, 1),
|
||||
birthday__lte=date(1999, 12, 31))
|
||||
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
list_filter = (DecadeBornListFilter,)
|
||||
|
||||
.. note::
|
||||
|
||||
As a convenience, the ``HttpRequest`` object is passed to the
|
||||
``lookups`` and ``queryset`` methods, for example::
|
||||
|
||||
class AuthDecadeBornListFilter(DecadeBornListFilter):
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
if request.user.is_superuser:
|
||||
return super().lookups(request, model_admin)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
if request.user.is_superuser:
|
||||
return super().queryset(request, queryset)
|
||||
|
||||
Also as a convenience, the ``ModelAdmin`` object is passed to
|
||||
the ``lookups`` method, for example if you want to base the
|
||||
lookups on the available data::
|
||||
|
||||
class AdvancedDecadeBornListFilter(DecadeBornListFilter):
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
"""
|
||||
Only show the lookups if there actually is
|
||||
anyone born in the corresponding decades.
|
||||
"""
|
||||
qs = model_admin.get_queryset(request)
|
||||
if qs.filter(birthday__gte=date(1980, 1, 1),
|
||||
birthday__lte=date(1989, 12, 31)).exists():
|
||||
yield ('80s', _('in the eighties'))
|
||||
if qs.filter(birthday__gte=date(1990, 1, 1),
|
||||
birthday__lte=date(1999, 12, 31)).exists():
|
||||
yield ('90s', _('in the nineties'))
|
||||
|
||||
* a tuple, where the first element is a field name and the second
|
||||
element is a class inheriting from
|
||||
``django.contrib.admin.FieldListFilter``, for example::
|
||||
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
list_filter = (
|
||||
('is_staff', admin.BooleanFieldListFilter),
|
||||
)
|
||||
|
||||
You can limit the choices of a related model to the objects involved in
|
||||
that relation using ``RelatedOnlyFieldListFilter``::
|
||||
|
||||
class BookAdmin(admin.ModelAdmin):
|
||||
list_filter = (
|
||||
('author', admin.RelatedOnlyFieldListFilter),
|
||||
)
|
||||
|
||||
Assuming ``author`` is a ``ForeignKey`` to a ``User`` model, this will
|
||||
limit the ``list_filter`` choices to the users who have written a book
|
||||
instead of listing all users.
|
||||
|
||||
You can filter empty values using ``EmptyFieldListFilter``, which can
|
||||
filter on both empty strings and nulls, depending on what the field
|
||||
allows to store::
|
||||
|
||||
class BookAdmin(admin.ModelAdmin):
|
||||
list_filter = (
|
||||
('title', admin.EmptyFieldListFilter),
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
The ``FieldListFilter`` API is considered internal and might be
|
||||
changed.
|
||||
|
||||
.. note::
|
||||
|
||||
The :class:`~django.contrib.contenttypes.fields.GenericForeignKey`
|
||||
field is not supported.
|
||||
|
||||
List filter's typically appear only if the filter has more than one choice.
|
||||
A filter's ``has_output()`` method controls whether or not it appears.
|
||||
|
||||
It is possible to specify a custom template for rendering a list filter::
|
||||
|
||||
class FilterWithCustomTemplate(admin.SimpleListFilter):
|
||||
template = "custom_template.html"
|
||||
|
||||
See the default template provided by Django (``admin/filter.html``) for
|
||||
a concrete example.
|
||||
At it's simplest ``list_filter`` takes a list or tuple of field names to
|
||||
activate filtering upon, but several more advanced options as available.
|
||||
See :ref:`modeladmin-list-filters` for the details.
|
||||
|
||||
.. attribute:: ModelAdmin.list_max_show_all
|
||||
|
||||
|
|
Loading…
Reference in New Issue