Magazine High tech

Filtres personnalisés pour le site d'admin de Django

Publié le 05 octobre 2010 par Luc

Pour faire suite à l'article sur la personnalisation de l'application admin de Django, voici un billet sur les filterspecs qui permettent de créer des filtres personnalisés dans l'admin. Cette fonctionnalité n'est pas très bien documentée mais en suivant des exemples, on peut en comprendre le fonctionnnement qui est relativement simple.

Vous avez peut-être déjà utilisé le champ list_filter d'un ModelAdmin, celui-ci permet de faire apparaître sur la partie droite de la page des liens qui vont permettre de filtrer les éléments d'une liste en fonction de la valeur de certains champs.

Exemple de filtre

Ce mécanisme est très simple. On définit simplement les champs pour lesquels créer un filtre via le list_filter.

Si on souhaite crée un filtre personnalisé, il est possible d'utiliser la notion de filterspec. Voici un bon exemple trouvé sur DjangoSnippet qui permet de filtrer les élements en fonction de leur 1ère lettre.

Les filterspecs sont définis dans le module django.contrib.admin.filterspecs. Il est possible d'en créer de nouveaux en dérivant de la classe FilterSpec ou d'une de ses classes dérivées comme ChoicesFilterSpec.

Le filterspec consiste en 3 méthodes principales à éventuellement redéfinir:

  • Le constructeur __init__ prend plusieurs paramètres :
    • field : le champ sur lequel sera appliqué le filtre
    • request : la requete Http envoyée pour afficher la page
    • params : les valeurs des filtres qui sont  passées en tant que paramètres dans l'url de la page à afficher.
    • model : le modèle de données auquel appartient le champ
    • model_admin : la classe ModelAdmin utilisée par le site d'admin pour afficher ce modèle

Ces éléments devraient être suffisants pour créer le filtre. model permettra en particulier d'accéder aux valeurs dans la liste via l'ORM de Django.

  • La méthode choices est appelée dans une boucle pour créer chaque élément du filtre. C'est un générateur Python qui prend en argument un champ de type ChangeList, la classe utilisée par le ModelAdmin pour l'accès aux paramètres. Elle retourne à chaque appel une valeur du filtre sous la forme d'un dictionnaire avec 3 clés:
    • selected : un booléen qui permet d'afficher le filtre en cours (un petit carré gris apparaît en face)
    • query_string : correspond à l'url de la page avec les paramètrs de filtre, par exemple /admin/app/model/?champ_a_filtrer=valeur_du_filtre. 
    • display: la valeur qui sera affichée dans le filtre pour l'élément

On définit ainsi quels sont les éléments à afficher.

  • La méthode title permet de définir le titre du filtre. Par défaut, si on ne la surcharge pas, elle retourne un titre construit avec l'attribut verbose_name du champ utilisé pour le filtre.

Une fois, un nouveau filterspec crée, il faut l'enregistrer c'est à dire l'insérer dans la liste disponible en tant que variable de la classe FilterSpec. On ajoute dans cette liste un tuple: une fonction de test et le filterspec lui-même. Lorsque le site d'admin crée les filtres il parcourt les éléments du list_filter du ModelAdmin à afficher. Pour chacun, il va chercher un FilterSpec parmi ceux enregistrés. il exécute alors la fonction de test correspondante. Si celle-ci retourne vrai alors le FilterSpec est utilisé.

La fonction de test prend en argument le champ à filtrer. Une astuce qui peut être utile: Créer un attribut sur le champ et vérifier l'existence de cet attribut par la fonction Python getattr. C'est ce qui est fait dans l'exemple du filtre alphabétique.

Cet exemple montre d'ailleurs très bien comment procéder pour créer son propre filterspec. Je vous le recommande chaudement.

Personnellement, j'ai utilisé ce mécanisme pour permettre de filtrer les éléments sur la valeur d'une clé étrangère. Malheureusement, le list_filter n'accepte pas par défaut ce type de filtres. Pour une application qui gère des concerts de musique, j'avais besoin de filtrer les concerts en fonction du type d'une formation. Le concert a un champ formation et la formation a un type. le filtre 'formation__type' ne fonctionne malheureusement pas.

J'ai donc crée un ForeignKey filterspec que j'ai essayé de rendre générique, ce besoin me paraissant assez commun. Le code est disponible sur DjangoSnippet. Je vous laisse le consulter, l'utiliser si besoin et éventuellement de me faire part de vos propositions d'amélioration.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Luc 11 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte