• Aucun résultat trouvé

Créons nos propres règles de validation

II. Premiers pas 32

9. Les formulaires 94

9.3. Créons nos propres règles de validation

Imaginons que nous, administrateurs du blog sur les crêpes bretonnes, recevions souvent des messages impolis des fanatiques de la pizza italienne depuis le formulaire de contact. Chacun ses goûts, mais nous avons d’autres chats à fouetter !

Pour éviter de recevoir ces messages, nous avons eu l’idée d’intégrer un filtre dans notre formulaire pour que celui-ci soit invalide si le message contient le mot « pizza ». Heureusement pour nous, il est facile d’ajouter de nouvelles règles de validation sur un champ. Il y a deux méthodes : soit le filtre ne s’applique qu’à un seul champ et ne dépend pas des autres, soit le filtre dépend des données des autres champs.

Pour la première méthode (la plus simple), il faut ajouter une méthode à la classeContactForm du formulaire dont le nom doit obligatoirement commencer par clean_, puis être suivi par le nom de la variable du champ. Par exemple, si nous souhaitons filtrer le champmessage, il faut ajouter une méthode semblable à celle-ci :

1 def clean_message(self):

2 message = self.cleaned_data['message']

3 if "pizza" in message:

4 raise

forms.ValidationError("On ne veut pas entendre parler de pizza !") 56 return message # Ne pas oublier de renvoyer le contenu du

champ traité

Nous récupérons le contenu du message comme depuis une vue, en utilisant l’attribut clea ned_dataqui retourne toujours un dictionnaire. Dès lors, nous vérifions si le message contient bien le mot « pizza », et si c’est le cas nous retournons une exception avec une erreur (il est important d’utiliser l’exception forms.ValidationError!). Django se servira du contenu de l’erreur passée en argument pour indiquer quel champ n’a pas été validé et pourquoi.

Le rendu HTML nous donne le résultat que vous pouvez observer sur la figure suivante, avec des données invalides après traitement du formulaire.

Maintenant, imaginons que nos fanatiques de la pizza italienne se soient adoucis et que nous ayons décidé d’être moins sévères, nous ne rejetterions que les messages qui possèdent le mot « pizza » dans le message et dans le sujet (juste parler de pizzas dans le message serait accepté).

Étant donné que la validation dépend de plusieurs champs en même temps, nous devons écraser la méthode clean héritée de la classe mère Form. Les choses se compliquent un petit peu :

1 def clean(self):

2 cleaned_data = super(ContactForm, self).clean() 3 sujet = cleaned_data.get('sujet')

4 message = cleaned_data.get('message') 5

6 if sujet and message: # Est-ce que sujet et message sont valides ?

7 if "pizza" in sujet and "pizza" in message:

8 raise forms.ValidationError(

9 "Vous parlez de pizzas dans le sujet ET le message ? Non mais ho !"

10 )

1112 return cleaned_data # N'oublions pas de renvoyer les données si tout est OK

Listing 50 – Notre fonction clean

La première ligne de la méthode permet d’appeler la méthode clean héritée de Form. En effet, si nous avons un formulaire d’inscription qui prend l’adresse e-mail de l’utilisateur, avant de vérifier si celle-ci a déjà été utilisée, il faut laisser Django vérifier si l’adresse e-mail est valide ou non. Appeler la méthode mère permet au framework de vérifier tous les champs comme d’habitude pour s’assurer que ceux-ci sont corrects, suite à quoi nous pouvons traiter ces données en sachant qu’elles ont déjà passé la validation basique.

La méthode mère clean va également renvoyer un dictionnaire avec toutes les données valides.

Dans notre dernier exemple, si l’adresse e-mail spécifiée était incorrecte, elle ne sera pas reprise dans le dictionnaire renvoyé. Pour savoir si les valeurs que nous souhaitons filtrer sont valides, nous utilisons la méthode get du dictionnaire qui renvoie la valeur d’une clé si elle existe, et renvoie None sinon. Par la suite, nous vérifions que les valeurs des variables ne sont pas à None (if sujet and message) et nous les traitons comme d’habitude.

Voici à la figure suivante ce que donne le formulaire lorsqu’il ne passe pas la validation que nous avons écrite.

Figure 9.2. – Formulaire invalide

Il faut cependant remarquer une chose : le message d’erreur est tout en haut et n’est plus lié aux champs qui n’ont pas passé la vérification. Si sujet et message étaient les derniers champs du formulaire, le message d’erreur serait tout de même tout en haut. Pour éviter cela, il est possible d’assigner une erreur à un champ précis :

1 def clean(self):

2 cleaned_data = super(ContactForm, self).clean() 3 sujet = cleaned_data.get('sujet')

4 message = cleaned_data.get('message') 5

6 if sujet and message: # Est-ce que sujet et message sont valides ?

7 if "pizza" in sujet and "pizza" in message:

8 msg =

"Vous parlez déjà de pizzas dans le sujet, n'en parlez plus dans le message !"

9 self.add_error("message", msg) 10

11 return cleaned_data

Le début est identique, en revanche, si les deux champs contiennent le mot « pizza », nous ne renvoyons plus une exception, mais nous définissons une liste d’erreurs à un dictionnaire (self._errors) avec comme clé le nom du champ concerné. Cette liste doit obligatoirement être le résultat d’une fonction de la classe mère Form (self.error_class) et celle-ci doit recevoir une liste de chaînes de caractères qui contiennent les différents messages d’erreur.

Une fois l’erreur indiquée, il ne faut pas oublier de supprimer la valeur du champ du dictionnaire, car celle-ci n’est pas valide. Rappelez-vous, un champ manquant dans le dictionnaire clea ned_datacorrespond à un champ invalide !

Et voici le résultat à la figure suivante.

Figure 9.3. – Le message d’erreur est bien adapté