L’API des formulaires¶
À propos de ce document
Ce document aborde en détails l’API des formulaires de Django. Il est recommandé de lire d’abord l’introduction à l’utilisation des formulaires.
Formulaires liés et non liés¶
Une instance Form
est soit liée (bound) à un jeu de données, soit non liée (unbound).
Si elle est liée à un jeu de données, elle est capable de valider ces données et d’afficher un formulaire en HTML en y incluant les données.
Si elle est non liée, elle ne peut pas procéder à la validation (car il n’y a aucune donnée à valider !), mais elle peut tout de même afficher un formulaire HTML vierge.
Pour créer une instance Form
non liée, instanciez la classe :
>>> f = ContactForm()
Pour lier des données au formulaire, transmettez ces données sous forme de dictionnaire comme premier paramètre au constructeur de la classe Form
:
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
Dans ce dictionnaire, les clés sont les noms de champs qui correspondent aux attributs de la classe Form
. Les valeurs sont les données que vous souhaitez valider. Il s’agit en principe de chaînes de caractères, mais ce n’est pas une obligation. Le type des données transmises dépend du champ Field
, comme nous allons le voir dans un moment.
- Form.is_bound¶
Si vous avez besoin de faire la différence entre des instances de formulaires liés et non liés au moment de l’exécution, vous pouvez vous baser sur l’attribut is_bound
du formulaire :
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({"subject": "hello"})
>>> f.is_bound
True
Notez que le fait de transmettre un dictionnaire vide crée un formulaire lié avec des données vides :
>>> f = ContactForm({})
>>> f.is_bound
True
Si vous souhaitez modifier d’une quelconque manière les données d’une instance Form
liée ou si vous aimeriez lier une instance Form
non liée à certaines données, créez une nouvelle instance de Form
. Il n’est pas possible de modifier les données dans une instance Form
. Dès qu’une instance Form
a été créée, ses données doivent être considérées comme immuables, que les données existent ou non.
Utilisation de formulaires pour valider des données¶
- Form.clean()¶
L’implémentation d’une méthode clean()
pour un formulaire se justifie lorsque de la validation personnalisée est nécessaire pour des champs interdépendants. Voir Nettoyage et validation de champs qui dépendent l’un de l’autre pour des exemples d’utilisation.
- Form.is_valid()¶
La tâche principale d’un objet Form
est de valider des données. Disposant d’une instance Form
liée, appelez la méthode is_valid()
pour procéder à la validation et renvoyer une valeur booléenne indiquant si les données sont valides :
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
Essayons avec certaines données non valides. Dans ce cas, subject
est vide (ce qui constitue une erreur, car tous les champs sont obligatoires par défaut) et sender
n’est pas une adresse électronique valable :
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
- Form.errors¶
Accédez à l’attribut errors
pour obtenir un dictionnaire des messages d’erreur :
>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
Dans ce dictionnaire, les clés correspondent aux noms de champs et les valeurs à des listes de chaînes représentant les messages d’erreur. Ceux-ci sont stockés dans des listes car un champ peut générer plusieurs messages d’erreur.
Vous pouvez accéder à errors
sans devoir appeler d’abord is_valid()
. Les données du formulaire seront validées lors du premier appel à is_valid()
ou du premier accès à errors
.
Les routines de validation ne sont appelées qu’une seule fois, même si vous appelez plusieurs fois is_valid()
ou que vous accédez plusieurs fois à errors
. Cela signifie que si la validation provoque des effets de bord, ceux-ci ne sont produits qu’une seule fois.
- Form.errors.as_data()¶
Renvoie un dictionnaire faisant correspondre les champs à leur instance ValidationError
originale.
>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
Utilisez cette méthode chaque fois qu’il y a besoin d’identifier une erreur par son code
. Cela permet des choses telles que la réécriture du message d’erreur ou l’écriture d’une logique personnalisée dans une vue lorsqu’une erreur donnée est présente. Elle peut également être utilisée pour sérialiser les erreurs dans un format personnalisé (par ex. XML) ; par exemple, as_json()
se base sur as_data()
.
La nécessité de la méthode as_data()
s’explique par la rétrocompatibilité. Précédemment, les instances ValidationError
étaient perdues dès le moment où leurs messages d’erreur étaient ajoutés dans leur état rendu au dictionnaire Form.errors
. Idéalement, Form.errors
aurait dû stocker les instances ValidationError
et des méthodes préfixées par as_
auraient pu produire leur rendu final, mais il a fallu procéder d’une manière inverse afin de ne pas casser du code qui s’attendait à trouver des messages d’erreur « finaux » dans Form.errors
.
- Form.errors.as_json(escape_html=False)¶
Renvoie les erreurs sérialisées en JSON.
>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}
Par défaut, as_json()
n’échappe pas son contenu. Si vous l’utilisez dans un contexte comme des requêtes AJAX vers une vue de formulaire où le client interprète la réponse et insère les erreurs dans la page, il faut vous assurer de bien échapper le contenu du côté client pour éviter l’éventualité d’une attaque de script intersite. Vous pouvez le faire en JavaScript avec element.textContent = errorText
ou en jQuery avec $(el).text(errorText)
(plutôt que sa fonction .html()
).
Si pour une raison précise vous ne souhaitez pas utiliser l’échappement du côté client, vous pouvez aussi définir escape_html=True
et les messages d’erreur seront échappés afin de pouvoir les utiliser directement en HTML.
- Form.errors.get_json_data(escape_html=False)¶
Renvoie les erreurs sous forme de dictionnaire prêt à être sérialisé en JSON. Form.errors.as_json()
renvoie du JSON sérialisé, alors que cette methode renvoie les données d’erreur avant leur sérialisation.
Le paramètre escape_html
possède le même comportement que pour Form.errors.as_json()
.
- Form.add_error(field, error)¶
Cette méthode permet d’ajouter des erreurs à des champs spécifiques depuis la méthode Form.clean()
elle-même ou carrément depuis l’extérieur du formulaire, par exemple depuis une vue.
Le paramètre field
est le nom du champ auquel les erreurs seront attribuées. Si sa valeur est None
, l’erreur est traitée comme une erreur non liée à un champ, et fera partie des erreurs renvoyées par Form.non_field_errors()
.
Le paramètre error
peut être une chaîne ou de préférence une instance de ValidationError
. Consultez Génération de ValidationError pour des conseils de bonnes pratiques lors de la définition d’erreurs de formulaire.
Notez que Form.add_error()
enlève automatiquement les champs correspondants du dictionnaire cleaned_data
.
- Form.has_error(field, code=None)¶
Cette méthode renvoie une valeur booléenne indiquant si un champ contient une erreur avec un code
d’erreur spécifique. Si code
vaut None
, la méthode renvoie True
si le champ contient n’importe quelle erreur.
Pour vérifier la présence d’erreurs non liées aux champs, indiquez NON_FIELD_ERRORS
dans le paramètre field
.
- Form.non_field_errors()¶
Cette méthode renvoie la liste des erreurs dans Form.errors
qui ne sont pas associées à un champ particulier. Cela comprend les erreurs ValidationError
qui sont générées dans Form.clean()
et les erreurs ajoutées par Form.add_error(None, "...")
.
Comportement des formulaires non liés¶
Il n’y a pas de raison de vouloir valider un formulaire sans données, mais pour que cela soit dit, voici ce qui se passe avec des formulaires non liés :
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
Valeurs initiales de formulaires¶
- Form.initial¶
Le paramètre initial
permet de déclarer des valeurs initiales des champs de formulaire au moment de l’exécution. Par exemple, il peut être intéressant de pré-remplir un champ username
avec le nom d’utilisateur de la session en cours.
Pour faire cela, utilisez le paramètre initial
d’une instance Form
. Quand il est présent, ce paramètre doit être un dictionnaire faisant correspondre des noms de champs à des valeurs initiales. N’incluez que les champs pour lesquels une valeur initiale existe ; il n’est pas nécessaire d’inclure tous les champs du formulaire. Par exemple :
>>> f = ContactForm(initial={"subject": "Hi there!"})
Ces valeurs ne sont affichées que pour les formulaires non liés et elles ne servent pas à fournir des valeurs par défaut si un champ particulier n’est pas renseigné.
Si un champ Field
définit initial
et que vous incluez initial
lors de la création du formulaire, c’est ce dernier qui prend le dessus. Dans cet exemple, initial
est renseigné à la fois au niveau du champ et au niveau de l’instance de formulaire, et c’est ce dernier qui a la priorité :
>>> from django import forms
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial="class")
... url = forms.URLField()
... comment = forms.CharField()
...
>>> f = CommentForm(initial={"name": "instance"}, auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" value="instance" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
- Form.get_initial_for_field(field, field_name)¶
Renvoie les données initiales d’un champ de formulaire. Il récupère les données de Form.initial
s’il y en a, sinon il recherche dans Field.initial
. Les valeurs exécutables sont évaluées.
Il est recommandé de privilégier BoundField.initial
par rapport à get_initial_for_field()
car l’interface de BoundField.initial` est plus simple. De plus, au contraire de get_initial_for_field()
, BoundField.initial
place ses valeurs en cache. C’est particulièrement utile lorsque les valeurs initiales sont exécutables et que leur résultat peut varier (par ex. datetime.now
or uuid.uuid4
) :
>>> import uuid
>>> class UUIDCommentForm(CommentForm):
... identifier = forms.UUIDField(initial=uuid.uuid4)
...
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
Contrôle des données de formulaires modifiées¶
- Form.has_changed()¶
Lorsque vous avez besoin de savoir quelles données de formulaire ont été modifiées en référence aux données initiales, utilisez la méthode has_changed()
du formulaire.
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False
Lorsque le formulaire est envoyé, il est reconstruit et les données d’origine sont fournies afin que la comparaison puisse être faite :
>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()
has_changed()
renvoie True
si les données de request.POST
diffèrent de celles qui ont été fournies dans initial
, sinon elle renvoie False
. Le résultat est produit en appelant Field.has_changed()
pour chaque champ du formulaire.
- Form.changed_data¶
L’attribut changed_data
renvoie une liste des noms de champs dont les valeurs dans les données liées au formulaire (habituellement request.POST
) diffèrent de celles fournies initialement dans initial
. La liste renvoyée est vide si les données sont parfaitement identiques.
>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
... print("The following fields changed: %s" % ", ".join(f.changed_data))
...
>>> f.changed_data
['subject', 'message']
Accès aux champs depuis le formulaire¶
- Form.fields¶
Vous pouvez accéder aux champs d’une instance de Form
depuis son attribut fields
:
>>> for row in f.fields.values():
... print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields["name"]
<django.forms.fields.CharField object at 0x7ffaac6324d0>
Vous pouvez modifier le champ et la classe BoundField
d’une instance Form
pour changer la façon dont il sera affiché dans le formulaire :
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
>>> f["subject"].label = "Topic"
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Topic:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
Faites attention de ne pas modifier l’attribut base_fields
car cette modification influencerait toutes les instances ContactForm
suivantes à l’intérieur du même processus Python :
>>> f.base_fields["subject"].label_suffix = "?"
>>> another_f = ContactForm(auto_id=False)
>>> another_f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject?</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
Accès aux données « nettoyées »¶
- Form.cleaned_data¶
Chaque champ d’une classe Form
a non seulement la responsabilité de valider ses données, mais aussi de les « nettoyer », c’est-à-dire les normaliser dans un format cohérent. C’est une fonction bien utile, parce que cela permet de saisir les données d’un champ de plusieurs manières, tout en conservant une cohérence au niveau de la donnée résultante.
Par exemple, DateField
normalise les saisies en un objet Python datetime.date
. Que le contenu transmis soit une chaîne au format '1994-07-15'
, un objet datetime.date
ou un autre format encore, DateField
transformera toujours ce contenu en objet datetime.date
, pour autant qu’il soit valide.
Après avoir créé une instance de Form
avec un jeu de données et l’avoir validé, il est possible d’accéder aux données nettoyées par son attribut cleaned_data
:
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
Notez que tout champ basé sur du texte, tel que CharField
ou EmailField
, nettoie toujours le contenu saisi pour en faire une chaîne de caractères. Nous aborderons les implications du codage plus loin dans ce document.
Si vos données ne sont pas toutes valides, le dictionnaire cleaned_data
ne contient que les champs valides :
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there'}
cleaned_data
ne contient toujours que des clés correspondant à des champs définis dans le formulaire, même si vous lui transmettez des données supplémentaires lors de la création du formulaire. Dans cet exemple, nous transmettons des données de champs supplémentaires au constructeur de ContactForm
, mais cleaned_data
ne contient que les données correspondant aux champs du formulaire :
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... "extra_field_1": "foo",
... "extra_field_2": "bar",
... "extra_field_3": "baz",
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
Lorsque le formulaire est valide, cleaned_data
inclut une clé et une valeur pour tous ses champs, même si les données ne contenaient pas de valeur pour certains champs facultatifs. Dans cet exemple, le dictionnaire de données ne contient pas de valeur pour le champ nick_name
, mais cleaned_data
l’intègre tout de même, avec une valeur vide :
>>> from django import forms
>>> class OptionalPersonForm(forms.Form):
... first_name = forms.CharField()
... last_name = forms.CharField()
... nick_name = forms.CharField(required=False)
...
>>> data = {"first_name": "John", "last_name": "Lennon"}
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}
Dans l’exemple ci-dessus, la valeur cleaned_data
de nick_name
correspond à une chaîne vide, car nick_name
est un champ CharField
, et ces champs considèrent la chaîne vide comme une valeur vide. Chaque type de champ connaît sa valeur vide, par exemple DateField
emploie None
au lieu de la chaîne vide. Pour plus de détails sur le comportement de chaque champ dans ce cas de figure, consultez la rubrique « Valeur vide » de chaque champ dans la section « Classes Field
intégrées » ci-dessous.
Il est possible d’écrire du code pour effectuer la validation de certains champs de formulaires (en fonction de leur nom) ou pour le formulaire entier (prenant en compte la combinaison de différents champs). Vous trouverez davantage d’informations à ce sujet dans La validation de formulaires et de champs.
Affichage des formulaires en HTML¶
La seconde tâche d’un objet Form
est de s’afficher lui-même au format HTML. Pour faire cela, affichez-le avec print
:
>>> f = ContactForm()
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself"></div>
Si le formulaire est lié à des données, le résultat HTML contiendra ces données comme il se doit. Par exemple, si un champ est représenté par un composant <input type="text">
, les données figureront dans l’attribut value
. Si un champ est représenté par un composant <input type="checkbox">
, le HTML produit contiendra checked
le cas échéant :
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" value="hello" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" value="Hi there" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" value="foo@example.com" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></div>
Cette production par défaut enveloppe chaque champ dans une <div>
. À relever :
Pour des raisons d’agilité, le résultat ne contient pas les balises
<form>
et</form>
, ni la balise<input type="submit">
. C’est à vous de les fournir.Chaque type de champ possède une représentation HTML par défaut.
CharField
est représenté par<input type="text">
etEmailField
par<input type="email">
.BooleanField(null=False)
est représenté par<input type="checkbox">
. Notez qu’il ne s’agit que de valeurs par défaut raisonnables ; il est possible de définir le code HTML produit par un champ spécifique en utilisant des composants, ce que nous expliquerons tout à l’heure.Le nom
name
HTML de chaque balise est directement dérivé de son nom d’attribut dans la classeContactForm
.L’étiquette textuelle de chaque champ (par ex.
'Subject:'
,'Message:'
et'Cc myself:'
) est généré à partir du nom de champ en convertissant tous les soulignements en espaces et en mettant en majuscule la première lettre. Encore une fois, il ne s’agit que de valeurs par défaut plus ou moins adéquates ; vous pouvez aussi définir ces étiquettes manuellement.Chaque étiquette textuelle est intégrée dans une balise HTML
<label>
qui se réfère à son champ de formulaire par son attributid
. La valeur de celui-ci est générée en ajoutant le préfixe'id_'
au nom du champ. Les attributsid
et les balises<label>
sont compris dans le HTML produit par défaut pour être conforme aux bonnes pratiques, mais vous pouvez modifier ce comportement.Le résultat utilise la syntaxe HTML5, avec l’en-tête
<!DOCTYPE html>
. Par exemple, les attributs booléens tels quechecked
sont préférés au style XHTMLchecked='checked'
.
Même si la production comme <div>
est le style par défaut quand on affiche un formulaire par print
, il est possible de personnaliser ce résultat en utilisant votre propre gabarit de formulaire qui peut être défini pour tout le site, par formulaire ou par instance. Voir Gabarits de formulaire réutilisables.
Rendu par défaut¶
Le rendu par défaut lorsque vous affichez un formulaire par print
utilise les méthodes et attributs suivants.
template_name
¶
- Form.template_name¶
Le nom du gabarit produit si le formulaire est forcé à une chaîne, par ex. via print(form)
ou dans un gabarit via {{ form }}
.
Par défaut, une propriété renvoyant la valeur form_template_name
du producteur. Vous pouvez la définir à un nom de gabarit afin de la surcharger pour une classe de formulaire particulière.
render()
¶
- Form.render(template_name=None, context=None, renderer=None)¶
La méthode de production est appelée par __str__
ainsi que par les méthodes Form.as_div()
, Form.as_table()
, Form.as_p()
et Form.as_ul()
. Tous les arguments sont facultatifs et valent par défaut :
template_name
:Form.template_name
context
: la valeur renvoyée parForm.get_context()
renderer
: la valeur renvoyée parForm.default_renderer
En passant template_name
, vous pouvez personnaliser le gabarit utilisé pour un seul appel.
get_context()
¶
- Form.get_context()¶
Renvoie le contexte de gabarit utilisé pour produire le formulaire.
Le contexte disponible est :
form
: le formulaire lié.fields
: tous les champs liés, exceptés les champs cachés.hidden_fields
: tous les champs liés cachés.errors
: toutes les erreurs de formulaire non liées aux champs ou liées à des champs cachés.
template_name_label
¶
- Form.template_name_label¶
Le gabarit utilisé pour produire la balise <label>
d’un champ, utilisé lors de l’appel à BoundField.label_tag()
/legend_tag()
. Ce gabarit peut être adapté pour chaque formulaire en surchargeant cet attribut ou plus généralement en surchargeant le gabarit par défaut, voir aussi Redéfinition des gabarits de formulaires intégrés.
Styles de production¶
L’approche recommandée pour changer le style de production des formulaires est de définir un gabarit de formulaire personnalisé soit pour tout le site, par formulaire ou par instance. Voir Gabarits de formulaire réutilisables pour des exemples.
Les fonctions utilitaires suivantes sont fournies par rétrocompatibilité et sont des raccourcis appelant Form.render()
et en lui passant une valeur template_name
particulière.
Note
Parmi les styles de gabarits et de production que Django met à disposition, le style par défaut as_div()
est recommandé par rapport aux versions as_p()
, as_table()
et as_ul()
, car ce gabarit exploite les balises <fieldset>
et <legend>
pour grouper les composants «input» liés et facilite leur lecture en naviguant avec les lecteurs d’écran.
Chaque utilitaire lie une méthode de formulaire à un attribut donnant le nom de gabarit approprié.
as_div()
¶
- Form.template_name_div¶
Le gabarit utilisé par as_div()
. Par défaut : 'django/forms/div.html'
.
- Form.as_div()¶
as_div()
produit le formulaire par une série de balises <div>
, avec chaque <div>
contenant un champ, tel que :
>>> f = ContactForm()
>>> f.as_div()
… produit du HTML comme :
<div>
<label for="id_subject">Subject:</label>
<input type="text" name="subject" maxlength="100" required id="id_subject">
</div>
<div>
<label for="id_message">Message:</label>
<input type="text" name="message" required id="id_message">
</div>
<div>
<label for="id_sender">Sender:</label>
<input type="email" name="sender" required id="id_sender">
</div>
<div>
<label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
</div>
as_p()
¶
- Form.template_name_p¶
Le gabarit utilisé par as_p()
. Par défaut : 'django/forms/p.html'
.
- Form.as_p()¶
as_p()
produit le formulaire par une série de balises <p>
, chacune contenant un champ :
>>> f = ContactForm()
>>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
as_ul()
¶
- Form.template_name_ul¶
Le gabarit utilisé par as_ul()
. Par défaut : 'django/forms/ul.html'
.
- Form.as_ul()¶
as_ul()
produit le formulaire par une série de balises <li>
, chacune contenant un champ. Les balises <ul>
et </ul>
ne sont pas comprises, ce qui vous donne la flexibilité de définir vous-même des attributs à la balise <ul>
:
>>> f = ContactForm()
>>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>
as_table()
¶
- Form.template_name_table¶
Le gabarit utilisé par as_table()
. Par défaut : 'django/forms/table.html'
.
- Form.as_table()¶
as_table()
produit le formulaire sous forme de <table>
HTML :
>>> f = ContactForm()
>>> f.as_table()
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
>>> print(f.as_table())
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>
Ajout de styles aux lignes de formulaire obligatoires ou erronées¶
- Form.error_css_class¶
- Form.required_css_class¶
Il est assez fréquent de devoir définir des styles particuliers s’appliquant aux lignes et champs de formulaire qui sont obligatoires ou qui contiennent des erreurs. Par exemple, on pourrait afficher en gras les lignes de formulaire obligatoires et afficher les erreurs en rouge.
La classe Form
offre plusieurs points d’entrée permettant d’ajouter des attributs class
aux lignes obligatoires ou aux lignes avec des erreurs : complétez les attributs Form.error_css_class
et Form.required_css_class
from django import forms
class ContactForm(forms.Form):
error_css_class = "error"
required_css_class = "required"
# ... and the rest of your fields here
Après avoir fait cela, les classes "error"
et "required"
seront attribuées aux lignes correspondantes. Le code HTML ressemblera à quelque chose comme :
>>> f = ContactForm(data)
>>> print(f)
<div class="required"><label for="id_subject" class="required">Subject:</label> ...
<div class="required"><label for="id_message" class="required">Message:</label> ...
<div class="required"><label for="id_sender" class="required">Sender:</label> ...
<div><label for="id_cc_myself">Cc myself:</label> ...
>>> f["subject"].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f["subject"].legend_tag()
<legend class="required" for="id_subject">Subject:</legend>
>>> f["subject"].label_tag(attrs={"class": "foo"})
<label for="id_subject" class="foo required">Subject:</label>
>>> f["subject"].legend_tag(attrs={"class": "foo"})
<legend for="id_subject" class="foo required">Subject:</legend>
Configuration du rendu des composants de formulaires¶
- Form.default_renderer¶
Indique le moteur de rendu à utiliser pour le formulaire. Vaut None
par défaut, ce qui signifie que le moteur de rendu par défaut défini dans le réglage FORM_RENDERER
sera utilisé.
Vous pouvez le définir comme attribut de classe lors de la déclaration du formulaire ou utiliser le paramètre renderer
de Form.__init__()
. Par exemple :
from django import forms
class MyForm(forms.Form):
default_renderer = MyRenderer()
ou :
form = MyForm(renderer=MyRenderer())
Notes sur le tri des champs¶
Dans les raccourcis as_p()
, as_ul()
et as_table()
, les champs sont affichés dans l’ordre de leur définition dans la classe de formulaire. Par exemple, dans l’exemple de ContactForm
, les champs sont définis dans l’ordre subject
, message
, sender
, cc_myself
. Pour changer cet ordre dans le résultat HTML, modifiez l’ordre dans lequel ces champs apparaissent dans la classe.
Il existe plusieurs autres manières de personnaliser cet ordre :
- Form.field_order¶
Par défaut, Form.field_order=None
, ce qui conserve l’ordre dans lequel les champs sont définis dans la classe de formulaire. Si field_order
est une liste de noms de champs, les champs sont ordonnés en fonction de cette liste et les champs restants sont ajoutés selon l’ordre par défaut. Les noms de champs de la liste qui sont inconnus sont ignorés. Cela permet de désactiver un champ dans une sous-classe en le définissant à None
sans avoir à redéfinir l’ordre.
Il est également possible d’utiliser le paramètre Form.field_order
d’une classe Form
pour remplacer l’ordre des champs. Si un formulaire Form
définit field_order
et que field_order
est inclus lors de l’instanciation de Form
, c’est ce dernier field_order
qui a la priorité.
- Form.order_fields(field_order)¶
Vous pouvez réorganiser les champs à tout moment en utilisant order_fields()
avec une liste de noms de champs comme dans field_order
.
Affichage des erreurs¶
Lorsque vous affichez un objet Form
lié à des données, le fait de l’afficher va automatiquement procéder à la validation du formulaire s’il ne l’a pas encore été, et le résultat HTML contiendra les erreurs de validation sous forme d’une section <ul class="errorlist">
à côté du champ. Le positionnement précis des messages d’erreur dépend de la méthode de rendu HTML utilisée :
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data, auto_id=False)
>>> print(f)
<div>Subject:
<ul class="errorlist"><li>This field is required.</li></ul>
<input type="text" name="subject" maxlength="100" required aria-invalid="true">
</div>
<div>Message:
<textarea name="message" cols="40" rows="10" required>Hi there</textarea>
</div>
<div>Sender:
<ul class="errorlist"><li>Enter a valid email address.</li></ul>
<input type="email" name="sender" value="invalid email address" required aria-invalid="true">
</div>
<div>Cc myself:
<input type="checkbox" name="cc_myself" checked>
</div>
Personnalisation du format de la liste d’erreurs¶
- class ErrorList(initlist=None, error_class=None, renderer=None)[source]¶
Par défaut, les formulaires utilisent
django.forms.utils.ErrorList
pour mettre en forme les erreurs de validation.ErrorList
est un objet de type liste oùinitlist
est la liste des erreurs. De plus, cette classe possède les attributs et méthodes suivantes.- error_class¶
Les classes CSS à utiliser lors de la production de la liste des erreurs. Les éventuelles classes indiquées sont ajoutées à la classe
errorlist
par défaut.
- renderer¶
Indique le moteur de rendu à utiliser pour
ErrorList
. VautNone
par défaut, ce qui signifie que le moteur de rendu par défaut défini dans le réglageFORM_RENDERER
sera utilisé.
- template_name¶
Le nom du gabarit utilisé lors de l’appel à
__str__
ourender()
. Par défaut, il s’agit de'django/forms/errors/list/default.html'
qui redirige en réalité sur le gabarit'ul.html'
.
- template_name_text¶
Le nom du gabarit utilisé lors de l’appel à
as_text()
. Par défaut, il s’agit de'django/forms/errors/list/test.html'
. Ce gabarit produit les erreurs sous forme de liste à puces.
- template_name_ul¶
Le nom du gabarit utilisé lors de l’appel à
as_ul()
. Par défaut, il s’agit de'django/forms/errors/list/ul.html'
. Ce gabarit produit les erreurs dans des balises<li>
enveloppées par<ul>
avec les classes CSS telles que définies parerror_class
.
- get_context()[source]¶
Renvoie le contexte pour la production des erreurs dans un gabarit.
Le contexte disponible est :
errors
: une liste des erreurs.error_class
: une chaîne de classes CSS.
- render(template_name=None, context=None, renderer=None)¶
La méthode
render
est appelée par__str__
en plus de la méthodeas_ul()
.Tous les arguments sont facultatifs et contiennent par défaut :
template_name
: la valeur renvoyée partemplate_name
context
: la valeur renvoyée parget_context()
renderer
: la valeur renvoyée parrenderer
- as_text()¶
Effectue le rendu de la liste des erreurs en utilisant le gabarit défini par
template_name_text
.
- as_ul()¶
Effectue le rendu de la liste des erreurs en utilisant le gabarit défini par
template_name_ul
.
Si vous souhaitez personnaliser le rendu des erreurs, cela peut se faire en surchargeant l’attribut
template_name
ou plus généralement en surchargeant le gabarit par défaut, voir aussi Redéfinition des gabarits de formulaires intégrés.
Un affichage plus fin¶
Les méthodes as_p()
, as_ul()
et as_table()
sont des raccourcis, ce ne sont pas les seules façons d’afficher une objet formulaire.
- class BoundField[source]¶
Utilisé pour afficher en HTML un champ unique d’une instance de
Form
ou pour accéder à ses attributs.La méthode
__str__()
de cet objet affiche le code HTML du champ.
Pour récupérer un seul BoundField
, employez la syntaxe de consultation de dictionnaire sur le formulaire en utilisant le nom du champ comme clé :
>>> form = ContactForm()
>>> print(form["subject"])
<input id="id_subject" type="text" name="subject" maxlength="100" required>
Pour obtenir tous les objets BoundField
, faites une boucle sur le formulaire :
>>> form = ContactForm()
>>> for boundfield in form:
... print(boundfield)
...
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
Le résultat HTML spécifique de chaque champ respecte le réglage auto_id
de l’objet formulaire :
>>> f = ContactForm(auto_id=False)
>>> print(f["message"])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id="id_%s")
>>> print(f["message"])
<input type="text" name="message" id="id_message" required>
Attributs de BoundField
¶
- BoundField.auto_id[source]¶
L’attribut HTML ID de cet objet
BoundField
. Renvoie une chaîne vide siForm.auto_id
vautFalse
.
- BoundField.data[source]¶
Cette propriété renvoie les données de cet objet
BoundField
extraites par la méthode de composantvalue_from_datadict()
, ouNone
si elle n’a pas été indiquée :>>> unbound_form = ContactForm() >>> print(unbound_form["subject"].data) None >>> bound_form = ContactForm(data={"subject": "My Subject"}) >>> print(bound_form["subject"].data) My Subject
- BoundField.errors[source]¶
Un objet apparenté à une liste qui s’affiche par une section
<ul class="errorlist">
lorsqu’il est affiché en HTML :>>> data = {"subject": "hi", "message": "", "sender": "", "cc_myself": ""} >>> f = ContactForm(data, auto_id=False) >>> print(f["message"]) <input type="text" name="message" required aria-invalid="true"> >>> f["message"].errors ['This field is required.'] >>> print(f["message"].errors) <ul class="errorlist"><li>This field is required.</li></ul> >>> f["subject"].errors [] >>> print(f["subject"].errors) >>> str(f["subject"].errors) ''
Lors du rendu d’un champ avec des erreurs, l’attribut
aria-invalid="true"
sera défini pour le composant du champ pour indiquer aux lecteurs d’écran qu’il y a une erreur.Changed in Django 5.0:La définition de l’attribut
aria-invalid="true"
lorsqu’un champ comporte une erreur a été ajoutée.
- BoundField.field¶
L’instance
Field
de la classe de formulaire que cet objetBoundField
adapte.
- BoundField.form¶
L’instance
Form
à laquelle est lié cet objetBoundField
.
- BoundField.html_name¶
Le nom qui sera utilisé dans l’attribut
name
du code HTML du composant. Il prend en compte la valeurprefix
du formulaire.
- BoundField.id_for_label[source]¶
Utilisez cette propriété pour produire l’identifiant de ce champ. Par exemple, si vous construisez manuellement une balise
<label>
dans un gabarit (sans tenir compte quelabel_tag()
/legend_tag()
le ferait très bien à votre place) :<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}
Par défaut, il s’agira du nom du champ préfixé par
id_
( »id_my_field
» dans l’exemple ci-dessus). Il est possible de modifier cet identifiant en renseignantattrs
pour le composant du champ. Par exemple, en définissant un champ comme ceci :my_field = forms.CharField(widget=forms.TextInput(attrs={"id": "myFIELD"}))
et en utilisant le gabarit ci-dessus, le résultat affiché donnera quelque chose comme :
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
- BoundField.initial[source]¶
Utilisez
BoundField.initial
pour obtenir les valeurs initiales d’un champ de formulaire. Les données sont obtenues deForm.initial
s’il y en a, sinon il recherche dansField.initial
. Les valeurs exécutables sont évaluées. Voir Valeurs initiales de formulaires pour des exemples.BoundField.initial
met en cache sa valeur calculée, ce qui est particulièrement utile lorsque les valeurs sont des résultats variables de fonctions exécutables (par ex.datetime.now
ouuuid.uuid4
) :>>> from datetime import datetime >>> class DatedCommentForm(CommentForm): ... created = forms.DateTimeField(initial=datetime.now) ... >>> f = DatedCommentForm() >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54) >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54)
Il est recommandé de privilégier attr:BoundField.initial par rapport à
get_initial_for_field()
.
Renvoie
True
si le composant de cet objetBoundField
est invisible.
- BoundField.label¶
L’étiquette
label
du champ. Utilisée dans les méthodeslabel_tag()
/legend_tag()
.
- BoundField.name¶
Le nom de ce champ dans le formulaire :
>>> f = ContactForm() >>> print(f["subject"].name) subject >>> print(f["message"].name) message
- BoundField.template_name[source]¶
- New in Django 5.0.
Le nom du gabarit produit avec
BoundField.as_field_group()
.Une propriété renvoyant la valeur de
template_name
, si elle est définie, ou defield_template_name
dans le cas contraire.
- BoundField.use_fieldset[source]¶
Renvoie la valeur de l’attribut
use_fieldset
du composant de ceBoundField
.
- BoundField.widget_type[source]¶
Renvoie le nom de classe en minuscules du composant du champ enveloppé, sans un éventuel suffixe
input
ouwidget
. Cela peut être utilisé lors de la construction de formulaires lorsque la disposition est dépendante du type de composant. Par exemple :{% for field in form %} {% if field.widget_type == 'checkbox' %} # render one way {% else %} # render another way {% endif %} {% endfor %}
Méthodes de BoundField
¶
- BoundField.as_field_group()¶
- New in Django 5.0.
Produit le champ avec
BoundField.render()
avec des valeurs par défaut, ce qui produit le champBoundField
, y compris son étiquette, son texte d’aide et d’éventuelles erreurs sur la base du gabarittemplate_name
si défini ou sinonfield_template_name
.
Renvoie une chaîne HTML pour représenter le champ sous forme de
<input type="hidden">
.**kwargs
est retransmis àas_widget()
.Cette méthode est essentiellement utilisée en interne. Il est préférable d’utiliser plutôt un composant (
widget
).
- BoundField.as_widget(widget=None, attrs=None, only_initial=False)[source]¶
Produit l’affichage du champ en s’appuyant sur le composant
widget
transmis, et en ajoutant d’éventuels attributs HTML transmis dansattrs
. Si aucun composant n’est fourni, c’est le composant par défaut du champ qui sera utilisé.only_initial
est utilisé en interne par Django et ne devrait pas être défini explicitement.
- BoundField.css_classes(extra_classes=None)[source]¶
Lorsque vous utilisez les raccourcis d’affichage de Django, les classes CSS sont utilisées pour indiquer les champs de formulaire obligatoires ou les champs contenant des erreurs. Si vous affichez manuellement les champs de formulaire, ces classes CSS sont disponibles par la méthode
css_classes
:>>> f = ContactForm(data={"message": ""}) >>> f["message"].css_classes() 'required'
Si vous souhaitez fournir des classes supplémentaires en plus des classes liées aux erreurs et aux champs obligatoires, il est possible d’indiquer ces classes en paramètre :
>>> f = ContactForm(data={"message": ""}) >>> f["message"].css_classes("foo bar") 'foo bar required'
- BoundField.get_context()[source]¶
- New in Django 5.0.
Renvoie le contexte de gabarit pour le rendu du champ. Dans le contexte disponible,
field
est l’instance du champ lié (BoundField
).
- BoundField.label_tag(contents=None, attrs=None, label_suffix=None, tag=None)[source]¶
Effectue le rendu d’une balise
label
d’un champ de formulaire en utilisant le gabarit défini parForm.template_name_label
.Le contexte disponible est :
field
: l’instance du champ liéBoundField
correspondant.contents
: par défaut, une chaîne concaténant attr:BoundField.label etForm.label_suffix
(oField.label_suffix
, si défini).attrs
: undict
contenantfor
,Form.required_css_class
etid
.id
est généré par les attributsattrs
du composant du champ ou parBoundField.auto_id
. Des attributs supplémentaires peuvent être fournis par l’argumentattrs
.use_tag
: une valeur booléenne qui vautTrue
si l’étiquette possède unid
. SiFalse
, le gabarit par défaut omet la balise.tag
: une chaîne facultative pour personnaliser la balise, contientlabel
par défaut.
Astuce
Dans votre gabarit,
field
est l’instance de champBoundField
. C’est ainsi que l’appel àfield.field
fait référence àBoundField.field
qui est le champ déclaré, par ex.forms.CharField
.Pour afficher séparément la balise
label
d’un champ de formulaire, on peut appeler sa méthodelabel_tag()
:>>> f = ContactForm(data={"message": ""}) >>> print(f["message"].label_tag()) <label for="id_message">Message:</label>
Si vous souhaitez personnaliser le rendu, cela peut se faire en surchargeant l’attribut
Form.template_name_label
ou plus généralement en surchargeant le gabarit par défaut, voir aussi Redéfinition des gabarits de formulaires intégrés.
- BoundField.legend_tag(contents=None, attrs=None, label_suffix=None)[source]¶
Appelle
label_tag()
avectag='legend'
pour produire l’étiquette avec des balises<legend>
. C’est utile lors de la production de composants boutons radios et cases à cocher mutliples où<legend>
peut être plus adéquat qu’une balise<label>
.
- BoundField.render(template_name=None, context=None, renderer=None)¶
- New in Django 5.0.
La méthode de rendu est appelée par
as_field_group
. Tous les arguments sont facultatifs et leur valeur par défaut est :template_name
:BoundField.template_name
context
: la valeur renvoyée parBoundField.get_context()
renderer
: la valeur renvoyée parForm.default_renderer
En passant
template_name
, vous pouvez personnaliser le gabarit utilisé pour un seul appel.
- BoundField.value()[source]¶
Utilisez cette méthode pour afficher la valeur brute d’un champ telle qu’elle serait contenue dans un composant
Widget
:>>> initial = {"subject": "welcome"} >>> unbound_form = ContactForm(initial=initial) >>> bound_form = ContactForm(data={"subject": "hi"}, initial=initial) >>> print(unbound_form["subject"].value()) welcome >>> print(bound_form["subject"].value()) hi
Personnalisation de BoundField
¶
Si vous avez besoin d’accéder à certaines informations supplémentaires d’un champ de formulaire dans un gabarit et que l’utilisation d’une sous-classe de Field
ne suffit pas, envisagez également la personnalisation de BoundField
.
Un champ de formulaire personnalisé peut remplacer get_bound_field()
:
- Field.get_bound_field(form, field_name)[source]¶
Accepte une instance de
Form
et le nom du champ. La valeur renvoyée sera utilisée pour accéder au champ dans un gabarit. Il s’agira très probablement d’une instance d’une sous-classe deBoundField
.
Si par exemple vous avez un champ GPSCoordinatesField
et que vous souhaitez pouvoir accéder à des informations supplémentaires sur les coordonnées dans un gabarit, cela pourrait être implémenté de cette façon :
class GPSCoordinatesBoundField(BoundField):
@property
def country(self):
"""
Return the country the coordinates lie in or None if it can't be
determined.
"""
value = self.value()
if value:
return get_country_from_coordinates(value)
else:
return None
class GPSCoordinatesField(Field):
def get_bound_field(self, form, field_name):
return GPSCoordinatesBoundField(form, self, field_name)
Il est maintenant possible d’accéder au pays dans un gabarit avec {{ form.coordinates.country }}
.
Liaison de fichiers téléversés avec un formulaire¶
Lorsqu’on a affaire à des champs de formulaire de type FileField
ou ImageField
, les choses se compliquent un peu plus.
Premièrement, pour pouvoir envoyer des fichiers, il est important que la balise <form>
du formulaire définisse correctement son attribut enctype
à "multipart/form-data"
:
<form enctype="multipart/form-data" method="post" action="/foo/">
Deuxièmement, au moment d’instancier le formulaire, il faut lier les données de type fichier. Ces données sont traitées de manière distincte des données habituelles de formulaire, ce qui fait que quand un formulaire contient un champ FileField
ou ImageField
, il doit recevoir un second paramètre au moment de faire la liaison entre le formulaire et les données. Ainsi, si nous étendons notre ContactForm
pour qu’il contienne un champ ImageField
nommé mugshot
, nous devons lier les données de fichier contenant l’image mugshot
:
# Bound form with an image field
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> file_data = {"mugshot": SimpleUploadedFile("face.jpg", b"file data")}
>>> f = ContactFormWithMugshot(data, file_data)
En pratique, vous allez généralement indiquer request.FILES
comme source des données de fichier (comme pour request.POST
représentant la source des données de formulaire) :
# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)
La construction d’un formulaire non lié ne change pas, il suffit d’omettre aussi bien les données de formulaire que les données de fichier :
# Unbound form with an image field
>>> f = ContactFormWithMugshot()
Détection des formulaires composites¶
- Form.is_multipart()¶
Si vous écrivez des vues ou des gabarits réutilisables, il peut arriver que l’on ne sache pas à l’avance si un formulaire est composite. La méthode is_multipart()
indique si le formulaire nécessite un codage composite lors de son envoi :
>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True
Voici un exemple de la façon d’utiliser cette méthode dans un gabarit :
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
<form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>
Formulaires et sous-classes¶
Si vous avez plusieurs classes Form
dont les champs sont partagés, il est possible d’utiliser l’héritage pour éviter la redondance.
Lorsque vous héritez d’une classe Form
personnalisée, la sous-classe résultante inclut tous les champs des ses classes parentes, suivis des champs définis dans la sous-classe.
Dans cet exemple, ContactFormWithPriority
contient tous les champs de ContactForm
plus un champ supplémentaire, priority
. Les champs de ContactForm
apparaissent en premier :
>>> class ContactFormWithPriority(ContactForm):
... priority = forms.CharField()
...
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>
<div>Priority:<input type="text" name="priority" required></div>
Il est possible d’hériter de plusieurs formulaires, en considérant ces formulaires comme des « mixins ». Dans cet exemple, BeatleForm
hérite à la fois de PersonForm
et de InstrumentForm
(dans cet ordre), et sa liste de champs contient les champs de ses classes parentes :
>>> from django import forms
>>> class PersonForm(forms.Form):
... first_name = forms.CharField()
... last_name = forms.CharField()
...
>>> class InstrumentForm(forms.Form):
... instrument = forms.CharField()
...
>>> class BeatleForm(InstrumentForm, PersonForm):
... haircut_type = forms.CharField()
...
>>> b = BeatleForm(auto_id=False)
>>> print(b)
<div>First name:<input type="text" name="first_name" required></div>
<div>Last name:<input type="text" name="last_name" required></div>
<div>Instrument:<input type="text" name="instrument" required></div>
<div>Haircut type:<input type="text" name="haircut_type" required></div>
Il est possible d’enlever de manière déclarative un champ Field
hérité d’une classe parente en définissant son nom à None
dans la sous-classe. Par exemple :
>>> from django import forms
>>> class ParentForm(forms.Form):
... name = forms.CharField()
... age = forms.IntegerField()
...
>>> class ChildForm(ParentForm):
... name = None
...
>>> list(ChildForm().fields)
['age']
Préfixes de formulaires¶
- Form.prefix¶
Une balise <form>
peut contenir plusieurs formulaires Django. Afin que chaque formulaire possède son propre espace de noms, utilisez le paramètre nommé prefix
:
>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother)
<div><label for="id_mother-first_name">First name:</label><input type="text" name="mother-first_name" required id="id_mother-first_name"></div>
<div><label for="id_mother-last_name">Last name:</label><input type="text" name="mother-last_name" required id="id_mother-last_name"></div>
>>> print(father)
<div><label for="id_father-first_name">First name:</label><input type="text" name="father-first_name" required id="id_father-first_name"></div>
<div><label for="id_father-last_name">Last name:</label><input type="text" name="father-last_name" required id="id_father-last_name"></div>
Le préfixe peut aussi être défini au niveau de la classe de formulaire :
>>> class PersonForm(forms.Form):
... ...
... prefix = "person"
...