Paramétrage avancé de l'interface d'administration

Nous allons voir sur cette page comment personnaliser notre interface d'administration grâce à un paramétrage « avancé » des fonctionnalités de scaffolding de Django.

Paramétrage de la langue

Vous aurez remarqué que par défaut, notre interface d'administration est en anglais. C'est n'est pas un problème lorsque l'on est parfaitement bilingue comme un étudiant en Miage, mais ça peut être rebutant pour certains, notamment pour les utilisateurs potentiels de notre application…

Nous allons simplement spécifier à Django que nous souhaitons que la langue du projet soit le français, afin qu'il adapte la machinerie.

Rien de plus simple : modifions notre fichier settings.py, pour mentionner la locale 'fr-FR' à l'aide de la constante LANGUAGE_CODE. Tant que nous somme dans l'internationalisation, plaçons également la constante TIME_ZONE à 'Europe/Paris' :

settings.py
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# In a Windows environment this must be set to your system time zone.
TIME_ZONE = 'Europe/Paris'

# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'fr-FR'

Sauvegardez le fichier, et c'est terminé ! Notre back-office est à présent intégralement en français :

Django internationalisation i18n

Les plus tatillons dirons « Hum, l'interface est en français, mais les noms de nos modèles restent en anglais… ». Certes. Pour fixer ceci, nous allons paramétrer nos modèles de manière à ce que leur nom affiché le soit en français :

models.py
class ProductBacklog(models.Model):
    # ...
    
    class Meta:
        verbose_name = 'Backlog de produit'
        verbose_name_plural = 'Backlogs de produit'

Nous avons ici défini de quelle manière doit s'afficher le nom du modèle, à l'infinitif et au pluriel.

 Qu'est-ce que c'est que cette classe dans une autre classe ? Vous serez peut-être surpris par cette notation : nous définissons une classe à l'intérieur de la classe de notre modèle ! On appelle ceci une classe « interne ». On ne fait que rarement cela en Python, même si c'est tout à fait possible et autorisé (la preuve !). C'est le moyen qu'ont choisi les concepteurs de Django pour isoler les méta-données des modèles (des données portant sur les modèles eux-mêmes), sans mélanger ces attributs aux attributs propres de chaque modèle. Le nom de cette classe interne, « Meta », est par ailleurs une pure convention de Django. Discutable probablement, mais efficace, et lisible.

L'objet de cette section n'est pas de parler en détail des problématiques d'internationalisation, mais Django offre bien évidemment des mécanismes permettant de gérer plusieurs langues, et de faire en sorte que l'interface d'admin s'adapte en fonction de la langue choisie, noms des modèles compris. Nous en reparlerons plus tard (fichiers de locales, labels de traduction, etc.).

Voici à présent à quoi ressemble notre back-office :

Noms des modèles en français Django

Personnalisation des listes

Dans toute interface d'administration, il est question de présenter des listes d'enregistrements (qui reflètent généralement les lignes des tables d'une base de données). Les back-offices générés par Django ne dérogent pas à cette règle.

Voici un extrait de notre interface d'administration des projets Scrum :

Présentation des listes d'admin Django par défaut

On retrouve une présentation classique comportant une liste d'objets, un bouton de création d'un nouvel objet, et la possibilité de sélectionner une ou plusieurs lignes pour y appliquer des actions (ex. : suppression).

Le problème est que la présentation de la liste n'est pas folichonne : chaque ligne est nommée « Project object », ce qui n'est absolument pas parlant. D'autre part, on aimerait disposer de quelques outils pratiques, notamment pour trier les objets, réaliser des recherche textuelles, etc. Voyons à présent comment mettre en place tout ceci.

Une meilleure présentation de la liste

Notre premier objectif est que Django n'affiche plus « Project object » comme ancre de lien vers la fiche d'un projet, mais plutôt le nom du projet. Pour cela, nous allons définir, dans notre modèle Project, une méthode __unicode__(), qui aura pour mission de retourner le nom à afficher pour un projet donné :

models.py
class Project(models.Model):
    name = models.CharField(max_length=150)
    # ...
    
    def __unicode__(self):
        return self.name

Nous spécifions très simplement que le nom à afficher est la valeur de l'attribut name de l'objet.

Remarque importante à destination des utilisateurs de Python 3 – En Python 3, remplacez cette méthode__unicode__() par une méthode nomée __str__().

Nous disposons à présent d'une présentation beaucoup plus agréable :

Personnalisation des noms d'objets Django admin

Ajout de colonnes

Pour le moment, une seule colonne est affichée. Nous aimerions pouvoir consulter, directement sur cet écran, quelle est l'équipe en charge de tel ou tel projet.

Nous allons pour cela modifier le fichier admin.py de notre application chistera, afin de surcharger quelques éléments de l'interface d'administration. Pour rappel, notre fichier admin.py ressemble pour le moment à ceci :

chistera/admin.py
# -*- coding: UTF-8 -*-
from django.contrib import admin
from chistera.models import *

admin.site.register(Team)
admin.site.register(Project)
admin.site.register(ProductBacklog)
admin.site.register(Sprint)
admin.site.register(UserStory)

Ceci revient simplement à dire à Django : « Je souhaite que tu prennes en charge les modèles Team, Project, ProductBacklog, Sprint et UserStory dans l'onterface d'administration de mon application chistera : fais au mieux ! ». Même s'il ne s'en sort pas mal, nous allons apporter quelques améliorations.

Pour chaque modèle pris en charge par django-admin, il est possible de définir une sous-classe de ModelAdmin, décrivant les personnalisations à apporter à l'interface. Voici une première version de cette classe de paramétrage pour notre modèle Project :

chistera/admin.py
class ProjectAdmin(admin.ModelAdmin):
    list_display = ('name', 'team')

Nous avons ici simplement spécifié à Django que la vue de liste doit comporter deux colonnes, name et team (les deux champs de notre modèle Project). Il nous reste à lui demander de prendre en compte ce paramétrage, en précisant le nom de notre classe lors de la déclaration du modèle en tant que candidat à l'administration : admin.site.register(Project, ProjectAdmin)

La liste complète des paramétrages possibles de cette manière est disponible sur la documentation de Django, section ModelAdmin options.

Voici donc le code complet de notre fichier chistera/admin.py suite à cette modification :

chistera/admin.py
# -*- coding: UTF-8 -*-
from django.contrib import admin
from chistera.models import *

class ProjectAdmin(admin.ModelAdmin):
    list_display = ('name', 'team')

admin.site.register(Project, ProjectAdmin)
admin.site.register(ProductBacklog)
admin.site.register(Sprint)
admin.site.register(UserStory)
admin.site.register(Team)

Notre liste comporte maintenant une nouvelle colonne (« triable ») qui présente l'équipe responsable du projet :

Ajout de colonnes dans les listes d'admin

Un champ de recherche

Nous aimerions disposer d'un champ de recherche textuelle, pour trouver un projet (ou des projets) par nom. Pour ce faire, nous ajoutons simplement un attribut search_fields à notre classe ProjectAdmin, qui représente un tuple de champs candidats pour la recherche textuelle :

chistera/admin.py
class ProjectAdmin(admin.ModelAdmin):
    # ...
    search_fields = ('name',)

Un champ de recherche apparaît maintenant tout en haut de la liste, et est fonctionnel sans aucun code supplémentaire !

Champ de recherche dans le back office

Mise en place de filtres

En plus de la recherche et du tri par colonnes, il est parfois intéressant de disposer de filtres sur les listes. Leur mise en place est tout aussi facile que le reste !

Définissons pour cela un attribut list_filter, qui représente un tuple de noms de champs candidats au filtrage :

chistera/admin.py
class ProjectAdmin(admin.ModelAdmin):
    # ...
    list_filter = ('team',)

Nous pouvons à présent filtrer nos projets par équipe :

Filtres dans l'espace d'administration

Administration « en ligne »

Nous disposons pour le moment d'un découpage assez basique : pour chaque modèle, un écran d'administration avec une liste et des formulaires de CRUD.

Cette stratégie est pratique pour des cas simple, mais dans certains cas il est plus pratique de disposer d'écrans permettant de manipuler les objets de plusieurs modèles à la fois : c'est là qu'interviennent les InlineModelAdmin !

Pour illustrer ceci concrètement, voici ce que nous allons faire. Il serait intéressant de pouvoir ajouter, modifier et supprimer des projets lors de la consultation de la fiche d'une équipe : voyons comment implémenter ceci.

La mise en place se déroule en deux étapes :

  • La spécification de la manière dont doit se présenter la vue intégrée à la vue plus générale (ici, nous allons décrire comment devra se présenter la sous-fiche des projets au sein de la fiche d'une équipe). C'est ce que l'on appelle un InlineModelAdmin.
  • La déclaration de la prise en compte de notre InlineModelAdmin dans le ModelAdmin de l'objet (ici, la prise en compte du InlineModelAdmin de Project dans le ModelAdmin de Team).

Dans notre fichier admin.py, nous commençons donc par déclarer notre InlineModelAdmin :

chistera/admin.py
class ProjectInline(admin.TabularInline):
    model = Project

Puis, nous le mentionnons via l'attribut inlines du ModelAdminTeamAdmin :

chistera/admin.py
class TeamAdmin(admin.ModelAdmin):
    inlines = (ProjectInline,)

Notre interface d'administration d'une équipe dispose maintenant d'une vision des projets de l'équipe, avec possibilité d'ajouter, modifier et supprimer des projets. Pratique !

InlineModelAdmin

Testez vos connaissances

Où définir la langue d'un projet Django (LANGUAGE_CODE = 'fr-FR') ?
  • Dans le contrôleur frontal du projet.
  • Dans le contrôleur frontal d'une des applications utilisées par le projet.
  • Dans le fichier settings.py du projet.
Dans LANGUAGE_CODE = 'fr-FR', LANGUAGE_CODE est…
  • Une variable
  • Une constante
  • Une classe abstraite
  • Un code secret
Que signifie admin.site.register(MyModel, MyAdminModel) ?
  • Rien !
  • Le modèle MyModel devra être pris en charge dans l'interface d'administration en prenant en compte les paramétrages définis par la classe MyAdminModel.
  • Le modèle MyModel devra être pris en charge dans l'interface d'administration en prenant en compte les paramétrages définis par le dictionnaire MyAdminModel.

Dans le code suivant, qu'est-ce que list_display ?

class ProjectAdmin(admin.ModelAdmin):
    list_display = ('name', 'team')
  • Un attribut de la classe ProjectAdmin.
  • Un attribut de la classe ModelAdmin.
  • Une méthode de la classe ModelAdmin permettant de spécifier les colonnes à afficher dans le tableau listant les objets de type ProjectAdmin dans l'interface d'administration.
Est-il possible en Django de créer ou modifier sur un seul écran d'administration plusieurs objets de différents modèles ?
  • Non
  • Oui en utilisant une classe abstraite.
  • Oui en utilisant un InlineModelAdmin.
  • Oui en modifiant l'application admin de Django.