12 KiB
title, author
title | author |
---|---|
Formulaires Django | Steve Kossouho |
Saisir des données avec les formulaires
Les formulaires HTML
Le web n'a pas toujours eu de formulaires, mais c'est arrivé très tôt; Le navigateur Mosaic du NCSA a introduit le concept dans sa version 2.0, publiée en novembre 1993. Ils sont rentrés de facto dans la spécification HTML, qui a subi quelques améliorations depuis.
En HTML, les formulaires permettent à l'utilisateur d'interagir et d'envoyer des données.
Django fournit une classe Form
{.python} qui permet de facilement construire des formulaires et d'abstraire leur validation du contenu.
Les formulaires avec Django
Pour pouvoir afficher des formulaires dans un site Django, il faut écrire des classes Python qui héritent d'une classe Form
{.python} se trouvant dans le module django.forms
{.python}. Les classes de formulaire décrivent les champs du formulaire à afficher, ainsi que leur type (texte, fichier, date, etc.) et leur contenu.
from datetime import date
from django import forms
class PersonForm(forms.Form):
"""Exemple de formulaire avec des champs de types divers."""
first_name = forms.CharField(max_length=32, label="first name")
last_name = forms.CharField(max_length=32, label="last name")
birth_date = forms.DateField(initial=date(1990, 1, 1), label="birthday")
number = forms.IntegerField(label="phone number")
password = forms.CharField(widget=forms.PasswordInput, max_length=50, label="password")
Les classes de formulaire déclarent des attributs dont les types sont des instances de classes héritant de Field
{.python}.
Documentation des champs de formulaires
Utilisation de formulaires
Une fois un formulaire défini, on peut l'utiliser dans une vue.
from django.shortcuts import render
from demonstration.forms import PersonForm
def view_person_form(request):
"""Utilisation du formulaire PersonForm."""
form = PersonForm(data=request.POST or None)
return render(request, "person-form.html", {"form": form})
Lorsqu'une vue affiche un formulaire, elle doit l'initialiser. Un objet de formulaire peut être initialisé de deux manieres :
PersonForm(data=request.POST)
{.python} si la requête HTTP est de typePOST
;PersonForm()
{.python} si la requête HTTP est de typeGET
.
Dans le premier cas, les données POST: dict
{.python} seront utilisées pour les associer à l'objet formulaire.
Dans le second cas, le formulaire sera vide et s'affichera sans contenu dans le template.
Utilisation des formulaires dans les templates
Lorsque l'on a instancié un objet Form
{.python}, nous l'envoyons au contexte du template.
Interpoler l'objet suffit à en afficher les champs. Les données liées à l'objet seront visibles dans les champs.
...
<body>
<h1>Person form</h1>
{# Envoyer le formulaire à l'URL courante avec la methode POST #}
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="OK">
</form>
</body>
Notez que vous avez la responsabilité de fournir la balise <form>
{.html} et la classique balise <input type="submit">
{.html} pour faire fonctionner le formulaire.
La représentation de l'objet Form
{.python} dans un template peut prendre plusieurs formes.
Syntaxe | Description |
---|---|
{{ form }} {.djangotemplate} |
Affiche les champs dans des balises <div> {.html}. |
{{ form.as_p }} {.djangotemplate} |
Affiche les champs dans des balises <p> {.html}. |
{{ form.as_table }} {.djangotemplate} |
Affiche les champs dans des balises <th> {.html} et <td> {.html}. |
Les options sont limitées mais suffisantes. Pour pouvoir utiliser un rendu compatible avec des bibliothèques CSS telles que Bootstrap, Bulma ou Foundation, on conseillera une application externe telle que django-crispy-forms.
Attention lors du rendu
Notez que l'interpolation d'un objet formulaire dans un template ne génère jamais les balises <form></form>
{.html} nécessaires,
ni même la classique balise <input type="submit">
{.html} pour faire fonctionner le formulaire.
La raison est simple : les classes de formulaires existent notamment pour simpler
l'affichage et la validation des données.
Définir la destination de l'envoi des données et les boutons de validation est de la responsabilité du programmeur.
Techniquement, cela permet par exemple de cumuler plusieurs objets formulaire dans la même balise <form>
{.html}, si besoin.
Validation des formulaires
Un objet Form
{.python} peut aussi gérer la validation des données. Par défaut, il est capable de s'assurer que tous les champs obligatoires
sont remplis et que les données sont valides. Nous verrons par la suite comment on peut personnaliser la validation.
Pour confirmer dans la vue que des données expédiées par un formulaire sont correctes, nous devons utiliser la méthode is_valid()
{.python}.
from django.shortcuts import render, redirect
from demonstration.forms import PersonForm
def view_person_form(request):
"""Utilisation du formulaire PersonForm."""
form = PersonForm(data=request.POST or None)
if form.is_valid():
... # Traitement des données
return redirect("accueil")
return render(request, "person-form.html", {"form": form})
La méthode is_valid()
La méthode is_valid()
{.python} d'un formulaire renvoie True
{.python} si toutes les données associées au formulaire sont correctes et False
{.python} sinon.
Cette méthode exécute plusieurs méthodes de validation de l'objet qui s'occupent entre autres de confronter les données entrantes et le type des champs.
Si le formulaire contient des données considérées incorrectes, l'objet contiendra des attributs .field_errors
{.python} et .non_field_errors
{.python} contenant des messages d'erreur,
qui seront automatiquement affichés dans le template pour alerter l'utilisateur.
<form action="" method="post" name="person-form">
{% csrf_token %}
<table class="form-table">
{{ form.as_table }}
<tr>
<th></th>
<td><input type="submit" name="submit-form" value="Validate"></td>
</tr>
</table>
</form>
Note : La balise {% csrf_token %}
{.djangotemplate} est une protection obligatoire lorsqu'on gère la méthode HTTP POST
. Django est configuré par défaut pour qu'une page demandée via la méthode POST
et qui ne possède pas une donnée de formulaire valide pour l'entrée csrf_middleware_token
provoque une erreur HTTP 403.
Validation de la saisie utilisateur
Django propose un système simple dans les formulaires permettant de contrôler la saisie de données des utilisateurs.
Le système consiste principalement à écrire des méthodes des types suivants :
def clean_<field_name>() -> Any
{.python} : vérifie l'entrée du champfield_name
def clean() -> dict
{.python} : vérifier l'entrée de tous les champs
Les méthodes clean_<field_name>()
{.python} renvoient une donnée Python correspondant au type du champ,
ou peuvent lever une exception de type ValidationError
si la donnée du champ est incorrecte ou non autorisée.
La méthode clean()
{.python}, elle, renvoie un dictionnaire de données, mais peut aussi vérifier
plusieurs champs à la fois. Elle peut également lever une ValidationError
{.python}.
Dans ce cas, le formulaire contient des données dans .non_field_errors
{.python}.
:::notes Jeter un œil à la documentation en ligne pour en voir un exemple. :::
from django import forms
class MyForm(forms.Form):
text = forms.CharField(max_length=10, label="Texte")
def clean_text(self):
return self.data["text"] # Va aller dans le dictionnaire `self.cleaned_data`
def clean(self):
return super().clean() # Renvoie un dictionnaire des données de champs "nettoyées"
Gestion des téléchargements et fichiers média
La gestion des téléchargements est toujours un cas un peu délicat, que Django permet de gérer.
Pour gérer le téléchargement, il faut remplir quelques conditions :
- Créer un répertoire
- Définir
settings.MEDIA_URL
{.python} etsettings.MEDIA_ROOT
{.python} - Avoir un formulaire HTML avec la méthode
POST
et le type d'encodage<form enctype="multipart/form-data">
{.html}.
Pour accéder aux fichiers uploadés :
- Ajouter
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
{.python} aux URLs du projet pour servir les fichiers (ne fonctionne pas sisettings.DEBUG = False
{.python})
MEDIA_ROOT = BASE_DIR / "media" # par exemple
MEDIA_URL = "/media/"
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path, include
urlpatterns = [
path("", view_index, name="index"),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
<form method="post" action="" enctype="multipart/form-data">
{% csrf_token %} {{ form }} <input type="submit" value="OK">
</form>
Le code (approximatif) à ajouter pour prendre en compte les fichiers média et pour pouvoir les afficher.
class UploadForm(forms.Form):
"""Example of file upload form."""
image = forms.ImageField(max_length=128, required=True, label="image")
def save_uploaded_file(request: HttpRequest) -> str:
"""Custom method to process the upload of an image."""
storage = FileSystemStorage()
image = request.FILES["image"] # l'attribut FILES est un dictionnaire de champs fichier
name = storage.save(None, image)
return storage.url(name)
Exemple de formulaire et d'une fonction à appeler pour sauver le fichier et récupérer son URL.
def view_upload_form(request: HttpRequest): # noqa
"""View for the upload form."""
form = UploadForm(request.POST, request.FILES) if request.method == "POST" else UploadForm()
if form.is_valid():
image_url = save_uploaded_file(request)
return render(request, "template.html", {"form": form, "image_url": image_url})
Exemple de vue utilisant notre formulaire
Divers : Formulaire de recherche
L'écriture d'un formulaire de recherche est régie par les étapes suivantes :
- Afficher (méthode GET) la page contenant uniquement le formulaire.
- Valider le formulaire vers la même page (POST ou GET).
- Si le formulaire est valide, ajouter les résultats au contexte du template.
- Dans le template, s'il y a des résultats, alors les afficher sous le formulaire. Sinon, n'afficher que le formulaire.
Divers : Applications tierces pour gérer les formulaires
Il existe plusieurs applications dédiées à un affichage avancé des formulaires. La plus aboutie et réputée est django-crispy-forms
.
pip install django-crispy-forms
Page officielle de la documentation du package
Divers : Applications tierces pour déboguer vos pages
Une application célèbre et utile pour déboguer vos pages en live est django-debug-toolbar
.
pip install django-debug-toolbar
Page officielle de la documentation du package