313 lines
12 KiB
TeX
313 lines
12 KiB
TeX
\chapter{Administration}
|
|
|
|
Cette partie est tellement puissante et performante, qu'elle pourrait laisser penser qu'il est possible de réaliser une application complète rien qu'en configurant l'administration. C'est faux.
|
|
|
|
L'administration est une sorte de tour de contrôle évoluée, un \emph{back office} sans transpirer; elle se base sur le modèle de données programmé et construit dynamiquement les formulaires qui lui est associé.
|
|
Elle joue avec les clés primaires, étrangères, les champs et types de champs par \href{https://fr.wikipedia.org/wiki/Introspection}{introspection}, et présente tout ce qu'il faut pour avoir du \href{https://fr.wikipedia.org/wiki/CRUD}{CRUD}, c'est-à-dire tout ce qu'il faut pour ajouter, lister, modifier ou supprimer des informations.
|
|
|
|
Son problème est qu'elle présente une courbe d'apprentissage asymptotique.
|
|
Il est \textbf{très} facile d'arriver rapidement à un bon résultat, au travers d'un périmètre de configuration relativement restreint.
|
|
Quoi que vous fassiez, il y a un moment où la courbe de paramétrage sera tellement ardue que vous aurez plus facile à développer ce que vous souhaitez ajouter en utilisant les autres concepts de Django.
|
|
|
|
Cette fonctionnalité doit rester dans les mains d'administrateurs ou de gestionnaires, et dans leurs mains à eux uniquement: il n'est pas question de donner des droits aux utilisateurs finaux (même si c'est extrêment tentant durant les premiers tours de roues).
|
|
Indépendamment de la manière dont vous allez l'utiliser et la configurer, vous finirez par devoir développer une "vraie" application, destinée aux utilisateurs classiques, et répondant à leurs besoins uniquement.
|
|
|
|
Une bonne idée consiste à développer l'administration dans un premier temps, en \textbf{gardant en tête qu'il sera nécessaire de développer des concepts spécifiques}.
|
|
Dans cet objectif, l'administration est un outil exceptionel, qui permet de valider un modèle, de créer des objets rapidement et de valider les liens qui existent entre eux.
|
|
|
|
C'est aussi un excellent outil de prototypage et de preuve de concept.
|
|
|
|
Elle se base sur plusieurs couches que l'on a déjà (ou on va bientôt)
|
|
aborder (suivant le sens de lecture que vous préférez):
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\item
|
|
Le modèle de données
|
|
\item
|
|
Les validateurs
|
|
\item
|
|
Les formulaires
|
|
\item
|
|
Les widgets
|
|
\end{enumerate}
|
|
|
|
\section{Le modèle de données}
|
|
|
|
Comme expliqué ci-dessus, le modèle de données est constité d'un ensemble de champs typés et de relations. L'administration permet de décrire les données qui peuvent être modifiées, en y associant un ensemble (basique) de permissions.
|
|
|
|
Si vous vous rappelez de l'application que nous avions créée dans la première partie, les URLs reprenaient déjà la partie suivante:
|
|
|
|
\begin{minted}{python}
|
|
from django.contrib import admin
|
|
from django.urls import path
|
|
from gwift.views import wish_details
|
|
|
|
urlpatterns = [
|
|
path('admin/', admin.site.urls),
|
|
[...]
|
|
]
|
|
\end{minted}
|
|
|
|
Cette URL signifie que la partie \texttt{admin} est déjà active et accessible à l'URL \texttt{\textless{}mon\_site\textgreater{}/admin}.
|
|
C'est le seul prérequis pour cette partie.
|
|
|
|
Chaque application nouvellement créée contient par défaut un fichier \texttt{admin.py}, dans lequel il est possible de déclarer quel ensemble de données sera accessible/éditable.
|
|
Ainsi, si nous partons du modèle basique que nous avions détaillé plus tôt, avec des souhaits et des listes de souhaits:
|
|
|
|
\begin{minted}{python}
|
|
# gwift/wish/models.py
|
|
|
|
from django.db import models
|
|
|
|
|
|
class WishList(models.Model):
|
|
name = models.CharField(max_length=255)
|
|
|
|
|
|
class Item(models.Model):
|
|
name = models.CharField(max_length=255)
|
|
wishlist = models.ForeignKey(WishList, on_delete=models.CASCADE)
|
|
\end{minted}
|
|
|
|
Nous pouvons facilement arriver au résultat suivant, en ajoutant
|
|
quelques lignes de configuration dans ce fichier \texttt{admin.py}:
|
|
|
|
\begin{minted}{python}
|
|
from django.contrib import admin
|
|
|
|
from .models import Item, WishList
|
|
|
|
|
|
admin.site.register(Item)
|
|
admin.site.register(WishList)
|
|
\end{minted}
|
|
|
|
|
|
\begin{itemize}
|
|
\item
|
|
Nous importons les modèles que nous souhaitons gérer dans l'admin
|
|
\item
|
|
Et nous les déclarons comme gérables. Cette dernière ligne implique aussi qu'un modèle pourrait ne pas être disponible du tout, ce qui n'activera simplement aucune opération de lecture ou modification.
|
|
\end{itemize}
|
|
|
|
Il nous reste une seule étape à réaliser: créer un nouvel utilisateur.
|
|
Pour cet exemple, notre gestion va se limiter à une gestion manuelle; nous aurons donc besoin d'un \emph{super-utilisateur}, que nous pouvons créer grâce à la commande \texttt{python\ manage.py\ createsuperuser}.
|
|
|
|
\begin{verbatim}
|
|
$ python manage.py createsuperuser
|
|
Username (leave blank to use 'fred'): fred
|
|
Email address: fred@root.org
|
|
Password: ******
|
|
Password (again): ******
|
|
Superuser created successfully.
|
|
\end{verbatim}
|
|
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics{images/django/django-site-admin.png}
|
|
\caption{Connexion au site d'administration}
|
|
\end{figure}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics{images/django/django-site-admin-after-connection.png}
|
|
\caption{Administration}
|
|
\end{figure}
|
|
|
|
\section{Quelques conseils de base}
|
|
|
|
\begin{enumerate}
|
|
\item
|
|
Surchargez la méthode \texttt{str(self)} pour chaque classe que vous aurez définie dans le modèle.
|
|
Cela permettra de construire une représentation textuelle pour chaque instance de votre classe.
|
|
Cette information sera utilisée un peu partout dans le code, et donnera une meilleure idée de ce que l'on manipule.
|
|
En plus, cette méthode est également appelée lorsque l'administration historisera une action (et comme cette étape sera inaltérable, autant qu'elle soit fixée dans le début).
|
|
\item
|
|
La méthode \texttt{get\_absolute\_url(self)} retourne l'URL à laquelle on peut accéder pour obtenir les détails d'une instance. Par exemple:
|
|
\end{enumerate}
|
|
|
|
\begin{minted}{python}
|
|
def get_absolute_url(self):
|
|
return reverse('myapp.views.details', args=[self.id])
|
|
\end{minted}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\item
|
|
Les attributs \texttt{Meta}:
|
|
\end{enumerate}
|
|
|
|
\begin{minted}{python}
|
|
class Meta:
|
|
ordering = ['-field1', 'field2']
|
|
verbose_name = 'my class in singular'
|
|
verbose_name_plural = 'my class when is in a list!'
|
|
\end{minted}
|
|
|
|
\begin{enumerate}
|
|
|
|
\item
|
|
Le titre:
|
|
|
|
\begin{itemize}
|
|
\item
|
|
Soit en modifiant le template de l'administration
|
|
\item
|
|
Soit en ajoutant l'assignation suivante dans le fichier
|
|
\texttt{urls.py}:
|
|
\texttt{admin.site.site\_header\ =\ "SuperBook\ Secret\ Area}.
|
|
\end{itemize}
|
|
\item
|
|
Prefetch
|
|
\end{enumerate}
|
|
|
|
\url{https://hackernoon.com/all-you-need-to-know-about-prefetching-in-django-f9068ebe1e60?gi=7da7b9d3ad64}
|
|
|
|
\url{https://medium.com/@hakibenita/things-you-must-know-about-django-admin-as-your-app-gets-bigger-6be0b0ee9614}
|
|
|
|
En gros, le problème de l'admin est que si on fait des requêtes
|
|
imbriquées, on va flinguer l'application et le chargement de la page. La
|
|
solution consiste à utiliser la propriété \texttt{list\_select\_related}
|
|
de la classe d'Admin, afin d'appliquer une jointure par défaut et et
|
|
gagner en performances.
|
|
|
|
\subsection{admin.ModelAdmin}
|
|
|
|
La classe \texttt{admin.ModelAdmin} que l'on retrouvera principalement
|
|
dans le fichier \texttt{admin.py} de chaque application contiendra la
|
|
définition de ce que l'on souhaite faire avec nos données dans
|
|
l'administration. Cette classe (et sa partie Meta)
|
|
|
|
\subsection{L'affichage}
|
|
|
|
Comme l'interface d'administration fonctionne (en trèèèès) gros comme un
|
|
CRUD auto-généré, on trouve par défaut la possibilité de :
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\item
|
|
Créer de nouveaux éléments
|
|
\item
|
|
Lister les éléments existants
|
|
\item
|
|
Modifier des éléments existants
|
|
\item
|
|
Supprimer un élément en particulier.
|
|
\end{enumerate}
|
|
|
|
Les affichages sont donc de deux types: en liste et par élément.
|
|
|
|
Pour les affichages en liste, le plus simple consiste à jouer sur la propriété \texttt{list\_display}.
|
|
|
|
Par défaut, la première colonne va accueillir le lien vers le formulaire d'édition.
|
|
On peut donc modifier ceci, voire créer de nouveaux liens vers d'autres éléments en construisant des URLs dynamiquement.
|
|
|
|
Voir aussi comment personnaliser le fil d'Ariane ?
|
|
|
|
\section{Filtres}
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\item
|
|
list\_filter
|
|
\item
|
|
filter\_horizontal
|
|
\item
|
|
filter\_vertical
|
|
\item
|
|
date\_hierarchy
|
|
\end{enumerate}
|
|
|
|
\section{Permissions}
|
|
|
|
On l'a dit plus haut, il vaut mieux éviter de proposer un accès à l'administration à vos utilisateurs.
|
|
Il est cependant possible de configurer des permissions spécifiques pour certains groupes, en leur autorisant certaines actions de visualisation/ajout/édition ou suppression.
|
|
|
|
Cela se joue au niveau du \texttt{ModelAdmin}, en implémentant les méthodes suivantes:
|
|
|
|
\begin{minted}{python}
|
|
def has_add_permission(self, request):
|
|
return True
|
|
|
|
def has_delete_permission(self, request):
|
|
return True
|
|
|
|
def has_change_permission(self, request):
|
|
return True
|
|
\end{minted}
|
|
|
|
On peut accéder aux informations de l'utilisateur actuellement connecté au travers de l'objet \texttt{request.user}.
|
|
|
|
\section{Relations}
|
|
|
|
\subsection{Relations 1-N}
|
|
|
|
Les relations 1-n sont implémentées au travers de formsets (que l'on a normalement déjà décrits plus haut). L'administration permet de les définir d'une manière extrêmement simple, grâce à quelques propriétés.
|
|
|
|
L'implémentation consiste tout d'abord à définir le comportement du type d'objet référencé (la relation -N), puis à inclure cette définition au niveau du type d'objet référençant (la relation 1-).
|
|
|
|
\begin{minted}{python}
|
|
class WishInline(TabularInline):
|
|
model = Wish
|
|
|
|
class Wishlist(admin.ModelAdmin):
|
|
...
|
|
inlines = [WishInline]
|
|
...
|
|
\end{minted}
|
|
|
|
Et voilà : l'administration d'une liste de souhaits (\emph{Wishlist}) pourra directement gérer des relations multiples vers des souhaits.
|
|
|
|
\subsection{Autocomplétion}
|
|
|
|
Parler de l'intégration de select2.
|
|
|
|
\section{Présentation}
|
|
|
|
Parler ici des \texttt{fieldsets} et montrer comment on peut regrouper des champs dans des groupes, ajouter un peu de JavaScript, ...
|
|
|
|
\section{Actions sur des sélections}
|
|
|
|
Les actions permettent de partir d'une liste d'éléments, et autorisent
|
|
un utilisateur à appliquer une action sur une sélection d'éléments. Par
|
|
défaut, il existe déjà une action de \textbf{suppression}.
|
|
|
|
Les paramètres d'entrée sont :
|
|
|
|
\begin{enumerate}
|
|
\def\labelenumi{\arabic{enumi}.}
|
|
\item
|
|
L'instance de classe
|
|
\item
|
|
La requête entrante
|
|
\item
|
|
Le queryset correspondant à la sélection.
|
|
\end{enumerate}
|
|
|
|
\begin{minted}{python}
|
|
def double_quantity(self, request, queryset):
|
|
for obj in queryset.all():
|
|
obj.field += 1
|
|
obj.save()
|
|
double_quantity.short_description = "Doubler la quantité des souhaits."
|
|
\end{minted}
|
|
|
|
|
|
Et pour informer l'utilisateur de ce qui a été réalisé, on peut aussi
|
|
lui passer un petit message:
|
|
|
|
\begin{minted}{python}
|
|
if rows_updated = 0:
|
|
self.message_user(request, "Aucun élément n'a été impacté.")
|
|
else:
|
|
self.message_user(request, "{} élément(s) mis à jour".format(rows_updated))
|
|
\end{minted}
|
|
|
|
\section{Documentation}
|
|
|
|
Nous l'avons dit plus haut, l'administration de Django a également la possibilité de rendre accessible la documentation associée à un modèle de données.
|
|
Pour cela, il suffit de suivre les bonnes pratiques, puis \href{https://docs.djangoproject.com/en/stable/ref/contrib/admin/admindocs/}{d'activer la documentation à partir des URLs}:
|
|
|