Opened 3 weeks ago
Closed 2 weeks ago
#36982 closed Bug (invalid)
Docs for `ModelAdmin.list_filter` don't mention bare `ListFilter` subclasses as a valid element type
| Reported by: | Alessio Bogon | Owned by: | Lakshya Prasad |
|---|---|---|---|
| Component: | Documentation | Version: | 6.0 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Accepted | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
The ModelAdmin List Filters documentation states that list_filter accepts three types of elements:
- 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
However, the actual implementation in ChangeList.get_filters() (source) accepts any callable — including bare ListFilter subclasses — and instantiates them with (request, lookup_params, self.model, self.model_admin):
if callable(list_filter): # This is simply a custom list filter class. spec = list_filter(request, lookup_params, self.model, self.model_admin)
The entire admin filter rendering pipeline (ChangeList.get_filters(), ChangeList.get_queryset(), and the admin_list_filter template tag) only uses methods defined on ListFilter itself: has_output(), expected_parameters(), queryset(), choices(), title, and template. Nothing requires SimpleListFilter specifically.
This means a direct ListFilter subclass (not going through SimpleListFilter) works perfectly fine:
class MyFilter(admin.ListFilter): title = "My Filter" parameter_name = "my_param" template = "admin/my_filter.html" def __init__(self, request, params, model, model_admin): super().__init__(request, params, model, model_admin) params.pop(self.parameter_name, None) def has_output(self): return True def expected_parameters(self): return [self.parameter_name] def queryset(self, request, queryset): return queryset def choices(self, changelist): yield {"selected": True, "display": "All"} class MyAdmin(admin.ModelAdmin): list_filter = [MyFilter]
This is useful when you need full control over the filter (custom template, multi-value parameters, etc.) without the constraints of SimpleListFilter's lookups()/value() API or FieldListFilter's field introspection.
The docs should generalise point 2 to say "A subclass of django.contrib.admin.ListFilter" instead of "A subclass of django.contrib.admin.SimpleListFilter".
Change History (6)
comment:1 by , 3 weeks ago
| Description: | modified (diff) |
|---|
comment:2 by , 3 weeks ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:4 by , 3 weeks ago
| Has patch: | set |
|---|
Pull request submitted: https://github.com/django/django/pull/20915
Has patch checked and ready for review.
comment:6 by , 2 weeks ago
| Has patch: | unset |
|---|---|
| Resolution: | → invalid |
| Status: | assigned → closed |
I don't think we should do this, because ListFilter isn't documented, and the only main branch of other documented subclasses doesn't work here:
admin.E114: The value of list_filter[n] must not inherit from FieldListFilter.
Thanks for the report. This looks like a valid documentation improvement.
The current docs for ModelAdmin.list_filter do not explicitly mention that
bare ListFilter subclasses can be used directly. Accepting the ticket so
a documentation clarification patch can be added.