﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36982	Docs for `ModelAdmin.list_filter` don't mention bare `ListFilter` subclasses as a valid element type	Alessio Bogon		"The [https://docs.djangoproject.com/en/6.0/ref/contrib/admin/filters/ 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()` ([https://github.com/django/django/blob/3180ddb3f532ef246d318d64225886b7c0593676/django/contrib/admin/views/main.py#L188-L190 source]) accepts **any callable** — including bare `ListFilter` subclasses — and instantiates them with `(request, lookup_params, self.model, self.model_admin)`:

{{{
#!python
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:

{{{
#!python
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`""."	Bug	new	Documentation	6.0	Normal				Unreviewed	0	0	0	0	0	0
