Utilisation des formulaires

À propos de ce document

Ce document présente une introduction aux fonctionnalités de formulaires avec Django. Pour plus de détails sur des aspects spécifiques de l’API des formulaires, consultez The Forms API, Form fields et Form and field validation.

django.forms est la bibliothèque de gestion des formulaires de Django.

Même s’il est possible de traiter les envois de formulaires en utilisant simplement la classe HttpRequest de Django, l’utilisation de la bibliothèque de formulaires se charge d’un certain nombre de tâches liées aux formulaires. Avec cette bibliothèque, vous pouvez :

  1. Afficher un formulaire HTML contenant des composants de formulaires générés automatiquement.

  2. Vérifier les données envoyées par un ensemble de règles de validation.

  3. Réafficher le formulaire en cas d’erreurs de validation.

  4. Convertir les données de formulaires envoyées dans les types Python appropriés.

Aperçu

La bibliothèque se base sur ces concepts :

Composant

Une classe correspondant à un composant de formulaire HTML, par exemple <input type="text"> ou <textarea>. L’affichage du composant en HTML est aussi pris en charge.

Champ

Une classe responsable de la validation, par exemple un champ EmailField qui s’assure que ses données correspondent à une adresse électronique valable.

Formulaire

Un ensemble de champs qui savent comment valider leur contenu et s’afficher sous forme de code HTML.

Fichiers statiques des formulaires

Les ressources CSS et JavaScript requises pour afficher un formulaire.

La bibliothèque est découplée des autres composants de Django, tels que la couche de base de données, les vues et les gabarits. Elle ne dépend que des réglages de Django, de quelques fonctions utilitaires de django.utils ainsi que des fonctions de localisation de Django (mais rien ne vous oblige à utiliser les fonctions de localisation quand vous utilisez cette bibliothèque).

Les objets Form

Un objet Form (formulaire) incorpore une série de champs de formulaires et un ensemble de règles de validation qui doivent être respectées pour que le formulaire soit accepté. Les classes de formulaires sont créées en héritant de django.forms.Form et font usage du style déclaratif qui devrait vous paraître familier si vous avez utilisé les modèles de base de données de Django.

Par exemple, considérons un formulaire servant à implémenter la fonctionnalité « Me contacter » sur un site Web personnel :

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

Un formulaire est composé d’objets Field (champs). Dans ce cas, notre formulaire possède quatre champs : subject, message, sender et cc_myself. CharField, EmailField et BooleanField ne sont que trois des types de champs disponibles ; une liste complète figure dans Form fields.

Si le formulaire sera utilisé pour directement ajouter ou modifier un modèle Django, vous pouvez utiliser un ModelForm pour éviter la duplication de la description de votre modèle.

Utilisation d’un formulaire dans une vue

La pratique standard du traitement d’un formulaire dans une vue ressemble à ceci :

from django.shortcuts import render
from django.http import HttpResponseRedirect

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render(request, 'contact.html', {
        'form': form,
    })

Il existe trois chemins de code possibles ici :

Formulaire envoyé ?

Données ?

Ce qui se passe

Non envoyé

Aucune

Le gabarit reçoit une instance non renseignée de ContactForm.

Envoyé

Données non valides

Le gabarit reçoit une instance renseignée de ContactForm.

Envoyé

Données valides

Les données valides sont traitées. Redirection vers une page « Merci ».

La distinction entre Bound and unbound forms est importante :

  • Un formulaire non renseigné n’a aucune donnée associée. Lorsqu’il est présenté à l’utilisateur, il sera vide ou ne contiendra que des valeurs par défaut.

  • Un formulaire renseigné contient des données envoyées et il est donc possible de lui demander si ces données sont valides. Si un formulaire renseigné non valide est affiché, il peut contenir des messages d’erreur intégrés indiquant à l’utilisateur quelles sont les données à corriger.

Gestion des envois de fichiers dans un formulaire

Pour savoir comment gérer les envois de fichiers avec votre formulaire, consultez Binding uploaded files to a form.

Traitement des données d’un formulaire

Quand is_valid() renvoie True, les données validées du formulaire se trouvent dans le dictionnaire form.cleaned_data. Ces données auront été gracieusement converties en types Python pour vous.

Note

À ce stade, vous pouvez toujours accéder directement aux données non validées dans request.POST, mais les données validées sont plus adéquates.

Dans l’exemple ci-dessus, cc_myself contient une valeur booléenne. De même, les champs tels que IntegerField et FloatField convertissent leurs valeurs en un type Python int et float, respectivement.

Les champs en lecture seule ne sont pas disponibles dans form.cleaned_data (et définir une valeur dans une méthode clean() personnalisée n’aura aucun effet). Ces champs sont affichés sous forme textuelle au lieu de zones de saisie, ce qui fait qu’ils ne sont pas renvoyés au serveur.

En poursuivant l’exemple précédent, voici comment les données de formulaires pourraient être traitées :

if form.is_valid():
    subject = form.cleaned_data['subject']
    message = form.cleaned_data['message']
    sender = form.cleaned_data['sender']
    cc_myself = form.cleaned_data['cc_myself']

    recipients = ['info@example.com']
    if cc_myself:
        recipients.append(sender)

    from django.core.mail import send_mail
    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/') # Redirect after POST

Astuce

Pour en savoir plus sur l’envoi de courriels à partir de Django, consultez Envoi de messages électroniques.

Affichage d’un formulaire dans un gabarit

Les formulaires sont conçus pour fonctionner avec le langage de gabarit de Django. Dans l’exemple ci-dessus, nous avons transmis l’instance de ContactForm au gabarit par l’intermédiaire de la variable de contexte form. Voici un exemple simple de gabarit :

<form action="/contact/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>

Le formulaire n’affiche que ses propres champs ; il vous revient d’écrire les balises englobantes <form> et le bouton d’envoi.

Si le formulaire permet d’envoyer des fichiers, prenez soin d’inclure enctype="multipart/form-data" dans la balise form. Si vous souhaitez écrire un gabarit générique fonctionnant avec ou sans envoi de fichier dans le formulaire, vous pouvez tester l’attribut is_multipart() du formulaire :

<form action="/contact/" method="post"
    {% if form.is_multipart %}enctype="multipart/form-data"{% endif %}>

Formulaires et protection contre le « Cross site request forgery » (CSRF)

Django est livré avec une protection simple d’emploi contre les attaques Cross-Site Request Forgery. Lors de l’envoi d’un formulaire par la méthode POST avec la protection CSRF active, vous devez utiliser la balise de gabarit csrf_token comme dans l’exemple précédent. Cependant, comme la protection CSRF n’est pas directement liée aux formulaires dans les gabarits, cette balise est omise dans les exemples suivants de ce document.

form.as_p affiche le formulaire avec chacun de ses champs de formulaire et de ses étiquettes correspondantes englobé dans un paragraphe. Voici ce que ça donne pour notre gabarit d’exemple :

<form action="/contact/" method="post">
<p><label for="id_subject">Subject:</label>
    <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_message">Message:</label>
    <input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label>
    <input type="text" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
<input type="submit" value="Submit" />
</form>

Notez que chaque champ de formulaire possède un attribut ID défini à id_<nom-du-champ> auquel fait référence la balise étiquette (<label>) accompagnant le champ. C’est important dans l’optique de rendre les formulaires accessibles par les aides techniques comme les logiciels de lecture d’écran. Vous pouvez aussi personnaliser la manière dont les étiquettes et les identifiants sont générés.

Vous pouvez également utiliser form.as_table pour afficher des lignes de tableau (vous devrez ajouter vous-même les balises <table>) et form.as_ul pour afficher des éléments de liste.

Personnalisation du gabarit de formulaire

Si le code HTML généré par défaut ne vous convient pas, vous pouvez personnaliser complètement la manière dont le formulaire est affiché en utilisant le langage de gabarit de Django. En partant de l’exemple précédent :

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
    <div class="fieldWrapper">
        {{ form.message.errors }}
        <label for="id_message">Your message:</label>
        {{ form.message }}
    </div>
    <div class="fieldWrapper">
        {{ form.sender.errors }}
        <label for="id_sender">Your email address:</label>
        {{ form.sender }}
    </div>
    <div class="fieldWrapper">
        {{ form.cc_myself.errors }}
        <label for="id_cc_myself">CC yourself?</label>
        {{ form.cc_myself }}
    </div>
    <p><input type="submit" value="Send message" /></p>
</form>

Chaque champ de formulaire nommé peut être affiché dans le gabarit en utilisant {{ form.nom_du_champ }}, ce qui produira le code HTML requis pour afficher le composant de formulaire. La syntaxe {{ form.nom_du_champ.errors }} affiche une liste des erreurs du formulaire, sous forme de liste non ordonnée. Cela pourrait ressembler à ceci :

<ul class="errorlist">
    <li>Sender is required.</li>
</ul>

La liste possède une classe CSS errorlist pour vous permettre de mettre en forme son apparence. Si vous souhaitez personnaliser davantage l’affichage des erreurs, vous pouvez le faire par une boucle :

{% if form.subject.errors %}
    <ol>
    {% for error in form.subject.errors %}
        <li><strong>{{ error|escape }}</strong></li>
    {% endfor %}
    </ol>
{% endif %}

Boucles sur champs de formulaires

Si vous utilisez le même code HTML pour tous vos champs de formulaire, vous pouvez réduire la duplication de code en effectuant une boucle sur chaque champ en utilisant l’opérateur {% for %}:

<form action="/contact/" method="post">
    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }}: {{ field }}
        </div>
    {% endfor %}
    <p><input type="submit" value="Send message" /></p>
</form>

Dans cette boucle, {{ field }} est une instance de BoundField. BoundField possède également les attributs suivants qui peuvent être utiles dans vos gabarits :

{{ field.label }}

L’étiquette du champ, par exemple Adresse de courriel.

{{ field.label_tag }}

L’étiquette du champ placée dans la balise HTML <label>, par exemple <label for="id_email">Adresse de courriel</label>

{{ field.id_for_label }}
The ID that will be used for this field (id_email in the example above). You may want to use this in lieu of label_tag if you are constructing the label manually. It’s also useful, for example, if you have some inline JavaScript and want to avoid hardcoding the field’s ID.
{{ field.value }}

La valeur du champ, par exemple quelqu-un@example.com

{{ field.html_name }}

Le nom du champ tel qu’il sera utilisé dans le nom de champ de la balise input. Il prend en compte le préfixe de formulaire, si celui-ci est défini.

{{ field.help_text }}

Tout texte d’aide associé au champ.

{{ field.errors }}

Affiche une liste <ul class="errorlist"> contenant toute erreur de validation correspondant à ce champ. Vous pouvez personnaliser la présentation des erreurs avec une boucle {% for error in field.errors %}. Dans ce cas, chaque objet de la boucle est une simple chaîne de caractères contenant le message d’erreur.

{{ field.is_hidden }}

Cet attribut vaut True si le champ de formulaire est un champ masqué, sinon il vaut False. Ce n’est pas particulièrement utile comme variable de gabarit, mais pourrait être utile dans des tests conditionnels tels que :

{% if field.is_hidden %}
   {# Do something special #}
{% endif %}
{{ field.field }}

L’instance Field de la classe de formulaire que cet objet BoundField adapte. Vous pouvez l’utiliser pour accéder aux attributs de Field, par exemple {{ char_field.field.max_length }}.

Boucles sur les champs masqués et visibles

Si vous affichez manuellement un formulaire dans un gabarit, sans faire appel à l’affichage par défaut des formulaires de Django, il peut être nécessaire de traiter différemment les champs <input type="hidden"> des autres champs non masqués. Par exemple, comme les champs masqués ne produisent pas de contenu visible, l’affichage de messages d’erreur « à côté » du champ pourrait générer de la confusion pour les utilisateurs ; il faut donc gérer différemment les erreurs de ce type de champ.

Django fournit deux méthodes qui permettent de tourner en boucle indépendamment sur les champs visibles et masqués : hidden_fields() et visible_fields(). Voici une modification d’un exemple précédent en utilisant ces deux méthodes :

<form action="/contact/" method="post">
    {# Include the hidden fields #}
    {% for hidden in form.hidden_fields %}
    {{ hidden }}
    {% endfor %}
    {# Include the visible fields #}
    {% for field in form.visible_fields %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }}: {{ field }}
        </div>
    {% endfor %}
    <p><input type="submit" value="Send message" /></p>
</form>

Cet exemple ne gère pas du tout les erreurs des champs masqués. En principe, une erreur dans un champ masqué est un signe de manipulation abusive d’un formulaire, puisque l’interaction normale avec un formulaire ne les modifie pas. Cependant, vous pourriez facilement ajouter aussi une forme d’affichage pour ces erreurs de formulaire.

Gabarits de formulaire réutilisables

Si votre site utilise la même logique d’affichage des formulaires à plusieurs endroits, vous pouvez réduire la duplication en enregistrant la boucle de formulaire dans un gabarit autonome et en employant la balise include afin de réutiliser ce contenu dans d’autres gabarits :

<form action="/contact/" method="post">
    {% include "form_snippet.html" %}
    <p><input type="submit" value="Send message" /></p>
</form>

# In form_snippet.html:

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }}: {{ field }}
    </div>
{% endfor %}

Si l’objet formulaire transmis au gabarit possède un nom différent dans le contexte, vous pouvez lui donner un alias en utilisant le paramètre with de la balise include:

<form action="/comments/add/" method="post">
    {% include "form_snippet.html" with form=comment_form %}
    <p><input type="submit" value="Submit comment" /></p>
</form>

Si vous constatez que vous reproduisez souvent ce même code, il vous faut peut-être envisager la création d’une balise d’inclusion personnalisée.

Sujets complémentaires

Cette page aborde les généralités, mais les formulaires peuvent faire bien d’autres choses encore :

Voir aussi

La référence des formulaires

Contient la référence d’API complète, y compris les champs de formulaire, les composants de formulaire et la validation des champs et des formulaires.