Filtros de lista para ModelAdmin

Las clases ModelAdmin pueden definir filtros de lista que aparecen en el lado derecho de la página de cambio de lista del admin, como se ilustra en la siguiente captura de pantalla:

../../../_images/list_filter.png

Para activar la filtración por campo, establece ModelAdmin.list_filter a una lista o tupla de elementos, donde cada elemento es uno de los siguientes tipos:

  • Un nombre de campo.

  • Una instancia de la clase django.contrib.admin.SimpleListFilter.

  • Un tupla de dos elementos que contiene el nombre de un campo y una instancia de la clase django.contrib.admin.FieldListFilter.

Consulte los ejemplos a continuación para discutir cada una de estas opciones para definir list_filter.

Usando un nombre de campo

La opción más simple es especificar los nombres de campos requeridos desde tu modelo.

Cada campo especificado debe ser una instancia de BooleanField, CharField, DateField, DateTimeField, IntegerField, ForeignKey o ManyToManyField, por ejemplo:

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

Los nombres de campos en list_filter también pueden abarcar relaciones usando el operador __ de búsqueda, por ejemplo:

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

Usando una instancia de SimpleListFilter

Para filtrado personalizado, puedes definir tu propia lista de filtros creando una clase que herede de django.contrib.admin.SimpleListFilter. Debes proporcionar los atributos title y parameter_name, y sobreescribir los métodos lookups y queryset, por ejemplo:

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]

Nota

Como conveniencia, se pasa la instancia del objeto HttpRequest a los métodos lookups y queryset, por ejemplo:

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

También como una comodidad, el objeto ModelAdmin se pasa al método lookups, por ejemplo si deseas basar los lookups en los datos disponibles:

Finalmente, si deseas especificar un tipo de filtro explícito para usar con un campo puedes proporcionar un elemento list_filter como una tupla de 2 elementos, donde el primer elemento es el nombre del campo y el segundo elemento es una clase que hereda de django.contrib.admin.FieldListFilter, por ejemplo:

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

Aquí el campo is_staff utilizará la clase BooleanFieldListFilter. Especificar solo el nombre del campo hará que los campos utilicen automáticamente el filtro apropiado en la mayoría de los casos, pero este formato permite controlar el filtro utilizado.

Los siguientes ejemplos muestran las clases de filtro disponibles que debes optar para usarlas.

Puedes limitar las opciones de un modelo relacionado a los objetos involucrados en esa relación utilizando RelatedOnlyFieldListFilter:

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

Asumiendo que author es una ForeignKey a un modelo User, esto limitará las opciones del filtro list_filter a los usuarios que han escrito un libro, en lugar de listar a todos los usuarios.

Puedes filtrar valores vacíos utilizando EmptyFieldListFilter, que puede filtrar tanto cadenas vacías como nulos, dependiendo de lo que el campo permite almacenar:

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

Al definir un filtro utilizando la búsqueda __in, es posible filtrar por cualquier grupo de valores. Necesitas sobrescribir el método expected_parameters y especificar el atributo lookup_kwargs con el nombre del campo apropiado. Por defecto, múltiples valores en la cadena de consulta se separan con comas, pero esto puede personalizarse mediante el atributo list_separator. El siguiente ejemplo muestra un filtro utilizando tal caracter como separador:

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]

Nota

El campo GenericForeignKey no está soportado.

Los filtros de lista suelen aparecer solo si el filtro tiene más de una opción. El método has_output() del filtro controla si aparece o no.

Es posible especificar un plantilla personalizada para renderizar un filtro de lista:

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

Consulte la plantilla predeterminada proporcionada por Django (admin/filter.html) para un ejemplo concreto.

Facetas

Por defecto, se pueden mostrar los conteos para cada filtro, conocidos como facetas, activando el botón correspondiente en la interfaz de administración. Estos conteos se actualizarán según los filtros aplicados actualmente. Consulte ModelAdmin.show_facets para obtener más detalles.