# Django 5.2.8 documentation

# `ModelAdmin` 列表过滤器

`ModelAdmin` 类可以定义出现在管理员的更改列表页面右侧边栏上的列表过滤器，如下面的截图所示：

要启用按字段过滤，将 `ModelAdmin.list_filter` 设置为一个元素列表或元组，其中每个元素都是以下类型之一：

- 一个字段名称。

- `django.contrib.admin.SimpleListFilter` 的子类。

- 一个包含字段名称和 `django.contrib.admin.FieldListFilter` 子类的 2 元组。

请查看以下示例，讨论了定义 `list_filter` 的每个选项。

## 使用字段名称

最简单的选项是指定你的模型中需要的字段名称。

每个指定的字段应该是 `BooleanField`、`CharField`、`DateField`、`DateTimeField`、`IntegerField`、`ForeignKey` 或 `ManyToManyField` 中的一个，例如：

    class PersonAdmin(admin.ModelAdmin):
        list_filter = ["is_staff", "company"]

`list_filter` 中的字段名也可以使用 `__` 查找来跨越关系，例如：

    class PersonAdmin(admin.UserAdmin):
        list_filter = ["company__name"]

## 使用 `SimpleListFilter`

对于自定义过滤，你可以通过子类化 `django.contrib.admin.SimpleListFilter` 来定义自己的列表过滤器。你需要提供 `title` 和 `parameter_name` 属性，并重写 `lookups` 和 `queryset` 方法，例如：

    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

为方便起见，`HttpRequest` 对象被传递给 `lookups` 和 `queryset` 方法，例如：

    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)

另外，为了方便起见，`ModelAdmin` 对象被传递给 `lookups` 方法，例如，如果你想根据现有数据进行查找：

    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"))

## 使用字段名称和显式的 `FieldListFilter`

最后，如果你希望为一个字段指定明确的过滤器类型，你可以提供一个包含 2 个元素的 `list_filter` 项，其中第一个元素是字段名称，第二个元素是继承自 `django.contrib.admin.FieldListFilter` 的类，例如：

    class PersonAdmin(admin.ModelAdmin):
        list_filter = [
            ("is_staff", admin.BooleanFieldListFilter),
        ]

这里"is_staff"字段将使用"BooleanFieldListFilter"。在大多数情况下，只指定字段名的字段会自动使用适当的过滤器，但这种格式允许您控制所使用的过滤器。

下面的示例显示了您需要选择使用的可用过滤器类别。

你可以使用 `RelatedOnlyFieldListFilter` 将相关模型的选择限制在该关系所涉及的对象上：

    class BookAdmin(admin.ModelAdmin):
        list_filter = [
            ("author", admin.RelatedOnlyFieldListFilter),
        ]

假设 `author` 是一个指向 `User` 模型的 `ForeignKey`，这将限制 `list_filter` 的选择只列出写过书的用户，而不是列出所有用户。

你可以使用 `EmptyFieldListFilter` 来过滤空值，它既可以过滤空字符串也可以过滤空值，这取决于字段允许存储的内容：

    class BookAdmin(admin.ModelAdmin):
        list_filter = [
            ("title", admin.EmptyFieldListFilter),
        ]

通过使用 `__in` 查询，可以过滤一组值中的任何一个。你需要重写 `expected_parameters` 方法，并指定具有适当字段名称的 `lookup_kwargs` 属性。默认情况下，查询字符串中的多个值将用逗号分隔，但可以通过 `list_separator` 属性进行自定义。以下示例显示了使用垂直竖线字符作为分隔符的过滤器：

    class FilterWithCustomSeparator(admin.FieldListFilter):
        # custom list separator that should be used to separate values.
        list_separator = "|"

        def __init__(self, field, request, params, model, model_admin, field_path):
            self.lookup_kwarg = "%s__in" % field_path
            super().__init__(field, request, params, model, model_admin, field_path)

        def expected_parameters(self):
            return [self.lookup_kwarg]

Note

不支持 `GenericForeignKey` 字段。

列表过滤器通常只在过滤器具有多个选择时才会出现。过滤器的 `has_output()` 方法控制它是否出现。

可以指定一个自定义模板来呈现列表过滤器：

    class FilterWithCustomTemplate(admin.SimpleListFilter):
        template = "custom_template.html"

具体的例子请看 Django 提供的默认模板（`admin/filter.html`）。

## Facets

默认情况下，可以通过在管理界面上切换打开来显示每个筛选器的计数，称为 facets。这些计数将根据当前应用的筛选器而更新。有关更多详细信息，请参阅 `ModelAdmin.show_facets`。