123 lines
5.6 KiB
TeX
123 lines
5.6 KiB
TeX
\chapter{Forms}
|
||
|
||
Ou comment valider proprement des données entrantes.
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/xkcd-327.png}}
|
||
\caption{XKCD 327}
|
||
\end{figure}
|
||
|
||
\begin{quote}
|
||
Le form, il s'assure que l'utilisateur n'a pas encodé de conneries et que l'ensemble reste cohérent.
|
||
Il (le form) n'a pas à savoir que tu as implémenté des closure tables dans un graph dirigé acyclique.
|
||
\end{quote}
|
||
|
||
Quand on parle de \texttt{forms}, on ne parle pas uniquement de formulaires Web.
|
||
On pourrait considérer qu'il s'agit de leur objectif principal, mais on peut également voir un peu plus loin: on peut en fait voir les \texttt{forms} comme le point d'entrée pour chaque donnée arrivant dans notre application: il s'agit en quelque sorte d'un ensemble de règles complémentaires à celles déjà présentes au niveau du modèle.
|
||
|
||
L'exemple le plus simple est un fichier \texttt{.csv} : la lecture de ce fichier pourrait se faire de manière très simple, en récupérant les valeurs de chaque colonne et en l'introduisant dans une instance du modèle.
|
||
|
||
Mauvaise idée.
|
||
On peut proposer trois versions d'un même code, de la version simple (lecture du fichier csv et jonglage avec les indices de colonnes), puis une version plus sophistiquée (et plus lisible, à base de \href{https://docs.python.org/3/library/csv.html\#csv.DictReader}{DictReader}), et la version + à base de form.
|
||
|
||
Les données fournies par un utilisateur \textbf{doivent} \textbf{toujours} être validées avant introduction dans la base de données.
|
||
Notre base de données étant accessible ici par l'ORM, la solution consiste à introduire une couche supplémentaire de validation.
|
||
|
||
Le flux à suivre est le suivant:
|
||
|
||
\begin{enumerate}
|
||
\item Création d'une instance grâce à un dictionnaire
|
||
\item Validation des données et des informations reçues
|
||
\item Traitement, si la validation a réussi.
|
||
\end{enumerate}
|
||
|
||
Ils jouent également deux rôles importants:
|
||
|
||
\begin{enumerate}
|
||
\item Valider des données, en plus de celles déjà définies au niveau du modèle
|
||
\item Contrôler le rendu à appliquer aux champs.
|
||
\end{enumerate}
|
||
|
||
Ils agissent come une glue entre l'utilisateur et la modélisation de vos structures de données.
|
||
|
||
\section{Flux de validation}
|
||
|
||
\textbar{} .Validation \textbar{} .is\_valid \textbar{} .clean\_fields ↓
|
||
.clean\_fields\_machin
|
||
|
||
Tout comme pour le modèle (+ ref), l'idée est simplement de définir plusieurs niveaux de validation.
|
||
|
||
\section{Dépendance avec le modèle}
|
||
|
||
Un \textbf{form} peut hériter d'une autre classe Django.
|
||
Pour cela, il suffit de fixer l'attribut \texttt{model} au niveau de la \texttt{class\ Meta} dans la définition.
|
||
|
||
\begin{minted}{python}
|
||
from django import forms
|
||
|
||
from wish.models import Wishlist
|
||
|
||
|
||
class WishlistCreateForm(forms.ModelForm):
|
||
class Meta:
|
||
model = Wishlist
|
||
fields = ('name', 'description')
|
||
\end{minted}
|
||
|
||
Notre form dépendra automatiquement des champs déjà déclarés dans la classe \texttt{Wishlist}.
|
||
Cela suit le principe de \texttt{DRY\ \textless{}don’t\ repeat\ yourself\textgreater{}\textasciigrave{}\_,\ et\ évite\ qu’une\ modification\ ne\ pourrisse\ le\ code:\ en\ testant\ les\ deux\ champs\ présent\ dans\ l’attribut\ \textasciigrave{}fields},
|
||
nous pourrons nous assurer de faire évoluer le formulaire en fonction du
|
||
modèle sur lequel il se base.
|
||
|
||
L'intérêt du form est de créer une isolation par rapport aux données provenant de l'extérieur.
|
||
|
||
\section{Rendu et affichage}
|
||
|
||
Le formulaire permet également de contrôler le rendu qui sera appliqué lors de la génération de la page.
|
||
Si les champs dépendent du modèle sur lequel se base le formulaire, ces widgets doivent être initialisés dans
|
||
l'attribut \texttt{Meta}.
|
||
Sinon, ils peuvent l'être directement au niveau du champ.
|
||
|
||
\subsection{Squelette par défaut}
|
||
|
||
\subsubsection{as\_p}
|
||
|
||
\subsubsection{as\_table}
|
||
|
||
\subsubsection{Vitor}
|
||
|
||
\subsection{Crispy-forms}
|
||
|
||
Comme on l'a vu à l'instant, les forms, en Django, c'est le bien.
|
||
Cela permet de valider des données reçues en entrée et d'afficher (très) facilement des formulaires à compléter par l'utilisateur.
|
||
|
||
Par contre, c'est lourd.
|
||
Dès qu'on souhaite peaufiner un peu l'affichage, contrôler parfaitement ce que l'utilisateur doit remplir,
|
||
modifier les types de contrôleurs, les placer au pixel près, \ldots
|
||
Tout ça demande énormément de temps.
|
||
Et c'est là qu'intervient \href{http://django-crispy-forms.readthedocs.io/en/latest/}{Django-Crispy-Forms}.
|
||
Cette librairie intègre plusieurs frameworks CSS (Bootstrap, Foundation et uni-form) et permet de contrôler entièrement le \textbf{layout} et la présentation.
|
||
|
||
(c/c depuis le lien ci-dessous)
|
||
|
||
Pour chaque champ, crispy-forms va :
|
||
|
||
\begin{itemize}
|
||
\item
|
||
utiliser le \texttt{verbose\_name} comme label.
|
||
\item
|
||
vérifier les paramètres \texttt{blank} et \texttt{null} pour savoir si le champ est obligatoire.
|
||
\item
|
||
utiliser le type de champ pour définir le type de la balise \texttt{\textless{}input\textgreater{}}.
|
||
\item
|
||
récupérer les valeurs du paramètre \texttt{choices} (si présent) pour la balise \texttt{\textless{}select\textgreater{}}.
|
||
\end{itemize}
|
||
|
||
\url{http://dotmobo.github.io/django-crispy-forms.html}
|
||
|
||
\section{Conclusions}
|
||
|
||
Toute donnée entrée par l'utilisateur, quelle qu'elle soit, \textbf{doit} passer par une instance de \texttt{form}: qu'il s'agisse d'un formulaire HTML, d'un fichier CSV, d'un parser XML, \ldots
|
||
Dès que des informations "de l'extérieur" font mine d'arriver dans le périmètre de votre application, il convient d'appliquer immédiatement des principes de sécurité reconnus.
|