Gestion avancée de l’interface d’administration
Polymorphisme
Pour visualiser le code relatif à ce chapitre : git diff v1.7.6..v1.8.0.
Interface d’administration
Pour visualiser précisément les changements, nous recommandons d’exécuter la commande suivante dans le projet fil rouge préalablement cloné :
$ git diff v1.7.4..v1.7.5 project/app/admin.py
Comme pour les modèles, il faut retravailler l’interface d’administration.
Pour commencer, il faut importer le nécessaire :
from polymorphic.admin import (
PolymorphicParentModelAdmin,
PolymorphicChildModelAdmin,
PolymorphicChildModelFilter,
)
Puis modifier la classe dont hérite PlayerAdmin. Nous avions cela :
@admin.register(Player)
class PlayerAdmin(admin.ModelAdmin):
...
En ceci :
class PlayerChildAdmin(PolymorphicChildModelAdmin):
base_model = Player
...
À noter que nous avons retiré le décorateur admin.register, puisque les classes filles ainsi que la classe mère seront enregistrées.
L’attribut base_model est implicite : il n’est pas obligatoire de le préciser, mais une déclaration explicite améliore la lisibilité.
La documentation indique que renommer fieldsets en base_fieldsets...
Multiples vues pour un modèle
Pour visualiser le code relatif à ce chapitre : git diff v1.8.0..v1.8.1.
1. Problématique
Nous souhaitons ici réaliser une interface nouvelle, à propos des jeux, pour un besoin différent. Dans notre cas, nous souhaitons avoir un tableau de statistiques à propos de nos jeux.
Nous souhaitons réutiliser les mécanismes performants de l’interface admin de Django, au lieu d’écrire une page totalement personnalisée, mais le souci est que Django ne permet qu’une seule interface d’administration pour un modèle donné.
Il n’est pas possible d’enregistrer deux fois le même modèle. Cette limitation peut toutefois être contournée grâce aux modèles proxy, qui constituent une représentation du modèle d’origine.
Voici comment faire ceci :
class StatGame(Game):
class Meta:
proxy=True
Nous créons un modèle qui hérite du modèle cible, puis nous ajoutons une configuration pour le déclarer comme proxy. Sans cette déclaration, Django traiterait ce modèle comme un héritage classique et créerait une table supplémentaire avec une relation un-à-un vers la table des jeux.
Cette déclaration n’entraîne aucune modification dans la base de données.
Maintenant que ce modèle est défini, nous pouvons créer une nouvelle classe admin chargée de l’enregistrer.
2. Détails de l’interface d’administration
Voici l’interface que nous souhaitons :
from .models import StatGame
@admin.register(StatGame)
class StatGameAdmin(admin.ModelAdmin):
search_fields...Filtres avancés
Pour visualiser le code relatif à ce chapitre : git diff v1.8.1..v1.8.2.
1. Filtres simples
Nous travaillions sur l’interface statistique des jeux ; nous allons maintenant ajouter un filtre pour trier selon le temps moyen nécessaire pour effectuer un jeu, en nous basant sur le nombre de questions. Nous voulons toutefois présélectionner les choix possibles :
-
vide ;
-
rapide ;
-
moyen ;
-
long.
Et nous souhaitons affecter à chaque notion une fourchette de nombre de questions. Nous allons donc écrire ceci :
from django.contrib.admin import SimpleListFilter
from django.utils.translation import gettext_lazy as gettext
class QuestionQuantityFilter(SimpleListFilter):
title = gettext("Quizz length")
parameter_name = "question_quantity"
def lookups(self, request, model_admin):
return (
("0", gettext("Void")),
("1-10", gettext("Quick")),
("11-25", gettext("Medium")),
("26-inf", gettext("Long")),
)
Le titre correspond au nom du filtre affiché à droite de l’écran. Le nom du paramètre est celui qui apparaîtra dans l’URL. Les valeurs de ce paramètre sont celles placées en première position dans les différents N-uplets renvoyés par la fonction lookups. En seconde position, on trouve le libellé affiché pour chacune de ces valeurs.
Nous ajoutons maintenant le code temporaire suivant :
def queryset(self, request, queryset):
return queryset
Maintenant que cette étape est terminée, nous pouvons tester le filtre. Pour l’utiliser, nous modifions le fichier app/admin.py :
from .filters import...Widgets
Pour visualiser le code relatif à ce chapitre : git diff v1.8.3..v1.8.3.
1. Améliorations pour le formulaire
Pour commencer, nous allons tester un certain nombre de fonctionnalités standard de l’interface d’administration, à l’aide de simples attributs.
Pour rappel, on peut créer une fonction qui renvoie le contenu souhaité, y compris du code HTML, ce qui est idéal pour personnaliser précisément l’affichage. Pour illustrer la syntaxe, on propose ceci :
from django.utils.html import format_html
from django.utils.lorem_ipsum import sentence
@register(Test)
class TestAdmin(ModelAdmin):
...
@display(description=gettext("lorem ipsum sentence"))
def lorem(self, obj):
return format_html("<p>{}</p>", sentence())
...
Nous n’oublierons pas de rajouter ce champ à l’attribut readonly_fields, et de le positionner dans fieldsets ou list_display, ou les deux.
Le résultat affiche une phrase aléatoire, ce qui montre qu’il est possible d’insérer le contenu souhaité à l’emplacement voulu, à condition d’utiliser les outils adaptés.
Nous avons abordé la notion de slug précédemment. Ici, nous demandons à l’interface admin de calculer automatiquement le slug à partir du libellé saisi :
@register(Test)
class TestAdmin(ModelAdmin):
...
prepopulated_fields = {
"slug": ("label",),
}
...
Nous pouvons tester la saisie du champ label dans l’interface et constater que le champ slug se remplit automatiquement. En cas de copier-coller, ce mécanisme ne se déclenche pas immédiatement : il faut cliquer sur le champ slug pour lancer le calcul. Il s’agit d’une fonctionnalité front-end.
Nous allons maintenant travailler sur les relations un-à-plusieurs et plusieurs-à-plusieurs. Tous les champs category* sont des relations un-à-plusieurs...
Vues
1. Personnaliser les vues
Pour visualiser le code relatif à ce chapitre : git diff v1.8.3..v1.8.4.
a. Définition de l’objectif
Pour rappel, dans le chapitre CRUD, nous avions décidé de ne voir que les jeux dans la liste de jeux. Puis en cliquant sur un jeu, nous avions accès à ce jeu et à la liste de questions relatives à ce jeu dans un inline.
Nous avions ensuite permis de rendre accessible le formulaire de la question qui lui-même avait un inline contenant les réponses relatives.
Seulement, l’interface était bancale, car une fois la question modifiée, si l’on cliquait sur enregistrer, on retombait sur la liste des questions (tous jeux confondus), c’est-à-dire le fonctionnement normal de Django.
De plus, le fil d’Ariane, lorsque l’on était dans le formulaire n’était pas consistant avec notre parcours de saisie de donnée, mais permettait aussi de retourner à cette liste de question.
Nous allons donc modifier tout cela pour rendre notre parcours de saisie de données cohérent.
b. Méthodologie appliquée
Dans l’interface d’administration, ouvrons un jeu, puis une question : c’est sur cette page que nous allons travailler.
Nous allons ensuite ouvrir notre barre d’outils et aller dans l’onglet Gabarits pour visualiser l’ensemble des gabarits utilisés. Il y en a beaucoup.
Nous sommes dans un formulaire, le gabarit associé à la vue est change_form.html. Lui-même hérite de base_site.html qui hérite à son tour de base.html.
Ce dernier met en place la structure générale du site. Nous pouvons cliquer sur le nom du gabarit pour consulter son code : la balise HTML placée au début confirme qu’il s’agit du gabarit maître.
Nous trouvons également la ligne suivante :
{% if not is_popup %}
Celle-ci permet de gérer les pop-ups (lorsque l’on clique sur des boutons pour ajouter ou modifier depuis un formulaire, une pop-up s’ouvre et elle n’a pas les menus et toute la décoration autour du formulaire).
Le code contient notamment les blocs branding, usertool, userlink et nav-breadcrumb. Ce dernier nom, littéralement « miettes de pain », correspond à la notion...
Intégration de Jazzmin
Pour visualiser le code relatif à ce chapitre : git diff v1.8.5..v1.8.6.
1. Installer Jazzmin
Pour commencer, nous allons installer l’outil suivant : django-jazzmin.
Pour cela, il existe deux options.
Pour la première, il faut démarrer une console, puis taper la commande :
$ make bash
$ poetry add django-jazzmin
Normalement, le fichier pyproject.toml ainsi que le fichier poetry.lock sont tous deux mis à jour.
La seconde méthode consiste à rajouter directement la ligne suivante dans pyproject.toml :
django-jazzmin = "^3.0.1"
Puis, il faut utiliser la commande suivante pour générer un fichier poetry.lock cohérent avec ce dernier :
$ make lock
Une fois ceci fait, quelle que soit la méthode, il faut reconstruire le conteneur :
$ make build
2. Paramétrer Jazzmin
Cette étape est particulièrement importante. Nous pouvons consulter la documentation officielle de Jazzmin, copier l’exemple pour partir d’une base solide, puis l’adapter. Les commentaires issus de la documentation ont été conservés afin de faciliter la compréhension. Le paramétrage est toutefois présenté par étapes, avec des explications progressives, car l’ensemble de la configuration tient dans un dictionnaire assez long.
Nous commençons par définir le titre affiché à différents endroits, afin d’aider l’utilisateur à se repérer :
JAZZMIN_SETTINGS = {
# title of the window (Will default to
current_admin_site.site_title if absent or None)
"site_title": gettext("Games Admin"),
# Title on the login screen (19 chars max) (defaults to
current_admin_site.site_header if absent or None)
"site_header": gettext("Games"),
# Title on the brand (19 chars max) (defaults to
current_admin_site.site_header if absent or None)
"site_brand": gettext("Games"),
Il est possible de rendre traductibles ces chaînes. On continue avec le paramétrage de logos que l’on peut visualiser à différents...