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 Alessio Bogon)

The ModelAdmin List Filters documentation states that list_filter accepts three types of elements:

  1. A field name
  2. A subclass of django.contrib.admin.SimpleListFilter
  3. 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 Alessio Bogon, 3 weeks ago

Description: modified (diff)

comment:2 by Lakshya Prasad, 3 weeks ago

Triage Stage: UnreviewedAccepted

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.

comment:3 by Lakshya Prasad, 3 weeks ago

Owner: set to Lakshya Prasad
Status: newassigned

I'd like to work on this ticket.

comment:4 by Lakshya Prasad, 3 weeks ago

Has patch: set

Pull request submitted: https://github.com/django/django/pull/20915

Has patch checked and ready for review.

comment:5 by Lakshya Prasad, 2 weeks ago

I’ve submitted a pull request for this issue:

https://github.com/django/django/pull/20941

comment:6 by Jacob Walls, 2 weeks ago

Has patch: unset
Resolution: invalid
Status: assignedclosed

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.

Note: See TracTickets for help on using tickets.
Back to Top