Filtrer un modèle dans l'administration de Django

Découvrez comment filtrer vos modèles dans l'interface d'administration de Django et comment afficher plusieurs fois le même modèle.

Publié le par Thibault Houdon (mis à jour le )
paceTemps de lecture estimé : 13 minutes

Il arrive qu'on ne souhaite pas afficher toutes les instances d'un modèle dans l'interface d'administration de Django.

Par exemple, vous pourriez avoir envie d'afficher uniquement les articles qui ne sont pas encore publiés.

Dans cet article, on va voir comment filtrer les modèles affichés dans l'interface d'administration de Django.

Il ne s'agit pas ici de rajouter des options de filtre que vous pouvez choisir dans l'interface d'administration mais bien de filtrer directement les instances à la source.

Nous verrons également qu'il est possible d'afficher plusieurs fois le même modèle avec différents filtres grâce aux modèles mandataires (« proxy model » en anglais).

Filtrer les instances d'un modèle

Pour filtrer les instances d'un modèle dans l'interface d'administration de Django, il suffit de surcharger la méthode get_queryset de la classe ModelAdmin.

Avant de surcharger la méthode, il est intéressant d'aller voir ce que fait la méthode d'origine qui se trouve dans le module django.contrib.admin.options :

# django/contrib/admin/options.py

class BaseModelAdmin(metaclass=forms.MediaDefiningClass):
    """Functionality common to both ModelAdmin and InlineAdmin."""

    ...

    def get_queryset(self, request):
        """
        Return a QuerySet of all model instances that can be edited by the
        admin site. This is used by changelist_view.
        """
        qs = self.model._default_manager.get_queryset()
        ordering = self.get_ordering(request)
        if ordering:
            qs = qs.order_by(*ordering)
        return qs

On remarque que Django utilise la méthode get_queryset du manager par défaut du modèle :

qs = self.model._default_manager.get_queryset()

Django change ensuite l'ordre des instances en utilisant les données contenues dans la requête et retourne un QuerySet :

ordering = self.get_ordering(request)
if ordering:
    qs = qs.order_by(*ordering)
return qs

Comme indiqué par la docstring, cette méthode retourne toutes les instances du modèle qui peuvent être éditées dans l'interface d'administration :

"""
Return a QuerySet of all model instances that can be edited by the
admin site. This is used by changelist_view.
"""

Pour n'afficher que certaines instances dans l'interface d'administration, il nous suffit donc de surcharger cette méthode et de filtrer le QuerySet grâce à la méthode filter.

# blog/admin.py

@admin.register(BlogPost)
class BlogPostAdmin(models.ModelAdmin):
    list_display = ('title', 'publish_date', 'published')

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.filter(published=True)

On commence par récupérer le QuerySet grâce à la fonction super en appelant la méthode get_queryset de la classe ModelAdmin :

qs = super().get_queryset(request)

On retourne ensuite ce QuerySet en le filtrant pour n'afficher que les articles publiés :

return qs.filter(published=True)

Aussi simple que ça 🙂

Afficher un modèle différent avec les proxy

Là où ça devient encore plus intéressant, c'est qu'on peut afficher plusieurs fois le même modèle avec différentes options de filtre grâce aux modèles mandataires (« proxy model »).

Sur Docstring par exemple, j'ai des articles qui sont destinés au blog et des articles uniquement à destination des parcours de formation.

Pour ces deux types d'articles, c'est le même modèle BlogPost qui est utilisé.

Je souhaite donc afficher deux entrées différentes dans l'interface d'administration.

La première chose à faire est donc de créer un modèle "proxy" :

# blog/models.py

class Post(BlogPost):
    class Meta:
        proxy = True
        verbose_name = "Article de formation"

Comme vous pouvez le voir dans le code ci-dessus, c'est super simple ! Il suffit de créer une classe qui hérite de votre modèle de base (BlogPost) et d'indiquer dans la classe Meta l'attribut proxy = True.

L'attribut verbose_name sert à indiquer la chaîne de caractères à afficher dans l'interface d'administration. Indispensable pour pouvoir différencier les modèles dans l'interface ⬇️

Maintenant que nous avons créé le modèle proxy, il ne reste plus qu'à l'ajouter dans le fichier admin.py comme on le ferait pour n'importe quel autre modèle :

# blog/admin.py

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'publish_date', 'published')

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.filter(blog=False)

J'utilise là encore la surcharge de get_queryset pour filtrer les articles et n'afficher que ceux dont le champ blog est False.

Et voilà, facile à mettre en place en quelques lignes de code. Vous pouvez bien entendu répéter l'opération avec un autre modèle proxy et d'autres options de filtre.