logo oujood
🔍

Validation des formulaires Django

is_valid() déclenche la validation, mais c'est vous qui définissez les règles. Django distingue la validation par champ avec clean_nomchamp(), la validation croisée avec clean(), et les validators réutilisables. Ce chapitre couvre chaque niveau avec des exemples concrets.

OUJOOD.COM

Comment fonctionne la validation dans Django

Quand vous appelez is_valid() sur un formulaire soumis, Django déclenche une chaîne de validation en plusieurs étapes :

  1. Validation du type de chaque champ — un IntegerField rejette les lettres.
  2. Validation des contraintes du champ — max_length, min_value, required.
  3. Exécution des validators déclarés sur le champ.
  4. Exécution de la méthode clean_nomchamp() si elle existe.
  5. Exécution de la méthode clean() pour la validation croisée.

Si une étape échoue, Django enregistre l'erreur dans form.errors et is_valid() retourne False.

Validation par champ avec clean_nomchamp()

Pour ajouter une règle de validation spécifique à un champ, définissez une méthode nommée clean_ suivi du nom du champ. Django l'appelle automatiquement après la validation de base.

  📋 Copier le code

from django import forms
from .models import Membres

class MembreForm(forms.Form):
    prenom = forms.CharField(max_length=255)
    nom    = forms.CharField(max_length=255)
    mail   = forms.EmailField()

    def clean_mail(self):
        mail = self.cleaned_data['mail']
        # Vérifier que l'email n'est pas déjà utilisé
        if Membres.objects.filter(mail=mail).exists():
            raise forms.ValidationError("Cette adresse email est déjà enregistrée.")
        return mail  # Toujours retourner la valeur nettoyée

    def clean_prenom(self):
        prenom = self.cleaned_data['prenom']
        # Interdire les prénoms trop courts
        if len(prenom) < 2:
            raise forms.ValidationError("Le prénom doit contenir au moins 2 caractères.")
        # Normaliser la casse
        return prenom.capitalize()

Deux règles à retenir : si la validation réussit, retournez toujours la valeur (éventuellement transformée). Si elle échoue, levez forms.ValidationError avec le message à afficher.

Validation croisée avec clean()

Certaines règles portent sur plusieurs champs à la fois — un mot de passe doit correspondre à sa confirmation, une date de fin doit être après une date de début. La méthode clean() s'exécute après tous les clean_champ() et reçoit l'ensemble des données nettoyées.

  📋 Copier le code

class InscriptionForm(forms.Form):
    prenom            = forms.CharField(max_length=255)
    mail              = forms.EmailField()
    mot_de_passe      = forms.CharField(widget=forms.PasswordInput)
    confirmation_mdp  = forms.CharField(widget=forms.PasswordInput,
                                        label='Confirmer le mot de passe')

    def clean(self):
        # super().clean() retourne les données nettoyées de tous les champs
        donnees = super().clean()
        mdp      = donnees.get('mot_de_passe')
        confirm  = donnees.get('confirmation_mdp')

        if mdp and confirm and mdp != confirm:
            # add_error attache l'erreur à un champ spécifique
            self.add_error('confirmation_mdp', "Les mots de passe ne correspondent pas.")

        return donnees

Validators réutilisables

Un validator est une fonction (ou un objet callable) qui reçoit une valeur et lève ValidationError si elle est invalide. L'avantage : il peut être réutilisé sur plusieurs champs ou plusieurs formulaires.

  📋 Copier le code

from django.core.exceptions import ValidationError

def valider_pas_de_chiffres(valeur):
    # Refuse les valeurs contenant des chiffres
    if any(c.isdigit() for c in valeur):
        raise ValidationError("Ce champ ne doit pas contenir de chiffres.")

class MembreForm(forms.Form):
    # Le validator est déclaré dans la liste validators= du champ
    prenom = forms.CharField(max_length=255,
                             validators=[valider_pas_de_chiffres])
    nom    = forms.CharField(max_length=255,
                             validators=[valider_pas_de_chiffres])

Validators intégrés de Django

Django fournit plusieurs validators prêts à l'emploi dans django.core.validators :

  📋 Copier le code

from django.core.validators import RegexValidator, MinLengthValidator

class MembreForm(forms.Form):
    # Numéro de téléphone français
    telephone = forms.CharField(
        validators=[RegexValidator(
            regex=r'^0[1-9][0-9]{8}$',
            message="Numéro de téléphone invalide (format : 0612345678)"
        )]
    )
    # Mot de passe d'au moins 8 caractères
    mot_de_passe = forms.CharField(
        widget=forms.PasswordInput,
        validators=[MinLengthValidator(8)]
    )

Afficher les erreurs dans le template

Quand is_valid() retourne False, Django re-rend le formulaire avec les erreurs. Elles s'affichent automatiquement avec {{ form.as_p }}, mais vous pouvez les contrôler finement :

  📋 Copier le code

<form method="post">
    {% csrf_token %}

    {% if form.errors %}
    <div class="erreurs">
        <!-- Erreurs globales (non liées à un champ) -->
        {{ form.non_field_errors }}
    </div>
    {% endif %}

    <div>
        {{ form.prenom.label_tag }}
        {{ form.prenom }}
        <!-- Erreurs spécifiques au champ prenom -->
        {% if form.prenom.errors %}
            <p class="erreur">{{ form.prenom.errors.0 }}</p>
        {% endif %}
    </div>

    <div>
        {{ form.mail.label_tag }}
        {{ form.mail }}
        {% if form.mail.errors %}
            <p class="erreur">{{ form.mail.errors.0 }}</p>
        {% endif %}
    </div>

    <button type="submit">Enregistrer</button>
</form>

form.non_field_errors affiche les erreurs générées dans clean() qui ne sont pas attachées à un champ précis. form.champ.errors.0 affiche le premier message d'erreur du champ.

Par carabde | Mis à jour le 05 mai 2025