Mis a jour le 2025-04-14, 12:10

Forms

Forms simples

Le principe est de définir une classe représentant le contenu d'une form (formulaire) html qui héritent de classe forms.Form :
  • exemple avec une form à 2 champs :
    from django import forms
    class MyForm(forms.Form):
        label = forms.CharField(label = 'Label', max_length = 5)
        description = forms.CharField(label = 'Description', max_length = 100, required = False)
        
  • label est le nom qui apparaît dans le formulaire et aussi dans l'attribut name du tag input.
  • utiliser required = False pour indiquer que le champ est optionnel lors du remplissage (sinon, par défaut, il est obligatoire).
  • l'objet form ne génère que les champs, pas les tags de la forme (<form>) ni le submit !
  • pour remplir certains champs à la construction : form = MyForm(initial = {'label': 'valeur'})
  • utilisation typique d'un objet Form dans une view :
    • attention : c'est la même fonction view qui présente la forme initiale, la forme remplie en cas d'erreur ou qui effectue le traitement des données soumises.
    • en cas d'erreur, l'objet Form est retourné rempli et donc, on peut le réutiliser pour représenter la form remplie à corriger.
    • une fois que form soumise est valide, les valeurs sont transférées dans le champ cleaned_data qui est un dictionnaire.
    def create(request):
        if request.method == 'POST':
            # si c'est une soumission de la form
            form = MyForm(request.POST)
            if form.is_valid():
                # on execute seulement si tout est ok dans la form
                myObj = MyObj() # Definition d'un objet modele
                myObj.label = form.cleaned_data['label']
                myObj.description = form.cleaned_data['description']
                return HttpResponseRedirect('/bravo') # Redirection vers une url
        else:
            # generation de la form initialement vide
            form = MyForm()
    
        # retour de la form, soit vide, soit remplie mais avec une erreur, avec un template
        return render(request, 'create.html', {'form': form})
        
  • côté template (ici create.html) :
    • l'url de l'action n'est pas mise en dure.
    • il faut mettre les tags form et input de type submit.
    • on peut choisir de formatter la form comme table (form.as_table), comme liste (form.as_ul) ou comme paragraphes (form.as_p).
    <form action="{% url myApp:createUrl' %}" method="post">
    {% csrf_token %}
    <table>
      {{ form.as_table }}
    </table>
    <div class="submit"><input type="submit" name="Créer"></div>
    </form>
        
  • on peut mettre les objets Form où l'on veut, comme par exemple dans un fichier forms.py (totalement libre).

Validation

Validation des champs d'un Form à la soumission : elle est déclenchée quand on appelle is_valid() sur la Form, et il y a de multiples possibilités :
  • d'abord, appel de la fonction validate de chaque champ : on peut la surcharger de la façon suivante en affectant une fonction à l'attribut validate qui doit lever l'exception ValidationError si le champ n'est pas bon :
    from django.core.exceptions import ValidationError
    class MyForm(forms.Form):
        label = forms.CharField(label = 'Label', max_length = 5)
        def myValidationFunction(value):
            ...
            raise ValidationError('my message')
        label.validate = myValidationFunction
        
  • puis appel des fonctions validator que l'on définit avec le tag validators et qui doivent aussi l'exception ValidationError si le champ n'est pas bon :
    from django.core.exceptions import ValidationError
    class MyForm(forms.Form):
        def myValidationFunction(value):
            sys.stderr.write('called validateLabel\n')
            if re.search('^[A-Z]+\d+$', value) is None:
                raise ValidationError('Format should be [A-Z]+\d+')
        label = forms.CharField(label = 'Label', max_length = 5, validators = [validateLabel])
        
  • puis, les champs sont transférés dans le dictionnaire cleaned_data.
  • puis, appel de la méthode clean_<nomChamp>. Cette fonction doit renvoyer le champ modifié ou non :
    class MyForm(forms.Form):
        label = forms.CharField(label = 'Label', max_length = 5)
        def clean_label(self):
            return self.cleaned_data['label'].strip()
        
  • enfin, appel de la méthode clean à la fin qui permet de traiter l'ensemble des champs de la forme, par exemple si interactions entre les champs :
    class MyForm(forms.Form):
        def clean(self):
            ...
        

ModelForms

Avec les Forms ci-dessus, on se retrouve souvent à recopier les champs de la form dans les champs du modèle quand la form correspond exactement à un modèle. Pour éviter cela, on peut utiliser les ModelForms.
ModelForm :
  • on définit une classe qui hérite de ModelForm et indique le modèle utilisé, ainsi que les champs à présenter :
    from django.forms import ModelForm
    class MyForm(ModelForm):
        class Meta:
            model = MyModel
            fields = ['label', 'description']
        
  • puis dans la view, on utilise ce ModelForm sans avoir besoin de recopier les champs dans le modèle et on peut directement utiliser save :
    def create(request):
        if request.method == 'POST':
            # si c'est une soumission de la form
            form = MyForm(request.POST)
            if form.is_valid():
                form.save()
                return HttpResponseRedirect('/bravo')
        else:
            # generation de la form initialement vide
            form = MyForm()
    
        # retour de la form, soit vide, soit remplie mais avec une erreur, avec un template
        return render(request, 'create.html', {'form': form})
        
  • on peut indiquer que tous les champs doivent être inclus en mettant fields = '__all__'.
  • on peut préciser certains champs (en reprenant le même nom que dans le modèle), avec par exemple le widget à utiliser :
    class MyForm(ModelForm):
        label = CharField(label = 'Label', max_length = 5, \
                          validators = [validateLabel], widget = TextInput(attrs = {'size': 5}))
        class Meta:
            model = MyModel
            fields = '__all__'
        

Copyright python-simple.com
programmer en python, tutoriel python, graphes en python, Aymeric Duclert