aboutsummaryrefslogtreecommitdiff
path: root/files/fr/learn/server-side/django/forms
diff options
context:
space:
mode:
authorjulieng <julien.gattelier@gmail.com>2021-11-14 14:30:47 +0100
committerSphinxKnight <SphinxKnight@users.noreply.github.com>2021-11-15 07:48:59 +0100
commitfaa96e657621455284245018b8a3b5050b613e6b (patch)
treea307a407e4b101b688fee89af9959001a9aae187 /files/fr/learn/server-side/django/forms
parente26d24940b2234a1a5e63b19d19d298bf36354e2 (diff)
downloadtranslated-content-faa96e657621455284245018b8a3b5050b613e6b.tar.gz
translated-content-faa96e657621455284245018b8a3b5050b613e6b.tar.bz2
translated-content-faa96e657621455284245018b8a3b5050b613e6b.zip
convert content to md
Diffstat (limited to 'files/fr/learn/server-side/django/forms')
-rw-r--r--files/fr/learn/server-side/django/forms/index.md702
1 files changed, 350 insertions, 352 deletions
diff --git a/files/fr/learn/server-side/django/forms/index.md b/files/fr/learn/server-side/django/forms/index.md
index 2d63644055..c2a244d739 100644
--- a/files/fr/learn/server-side/django/forms/index.md
+++ b/files/fr/learn/server-side/django/forms/index.md
@@ -13,214 +13,214 @@ tags:
- server side
translation_of: Learn/Server-side/Django/Forms
---
-<div>{{LearnSidebar}}</div>
+{{LearnSidebar}}{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}
-<div>{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</div>
-
-<p>Dans cette formation nous allons vous montrer comment travailler avec les formulaires HTML sous Django afin de créer, modifier et supprimer des instances de modèle. Pour illustrer le raisonnement, nous allons étendre le site web <a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> pour permettre aux bibliothécaires d'utiliser nos formulaires (plutôt que l'application d'administration par défaut) pour prolonger la durée de prêt des livres, et également pour ajouter, mettre à jour et supprimer des auteurs. </p>
+Dans cette formation nous allons vous montrer comment travailler avec les formulaires HTML sous Django afin de créer, modifier et supprimer des instances de modèle. Pour illustrer le raisonnement, nous allons étendre le site web [LocalLibrary](/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website) pour permettre aux bibliothécaires d'utiliser nos formulaires (plutôt que l'application d'administration par défaut) pour prolonger la durée de prêt des livres, et également pour ajouter, mettre à jour et supprimer des auteurs.
<table class="standard-table">
- <tbody>
- <tr>
- <th scope="row">Prérequis:</th>
- <td>Avoir terminé les formations précédentes, y compris <a href="/fr/docs/Learn/Server-side/Django/authentication_and_sessions">Django Tutorial Part 8: User authentication and permissions</a>.</td>
- </tr>
- <tr>
- <th scope="row">Objectifs:</th>
- <td>Comprendre comment écrire des formulaires pour récupérer des informations de la part des utilisateurs et mettre à jour la base de données. Comprendre commment il est possible de simplifier grandement la création de formulaires si l 'on travaille avec un seul modèle en utilisant les vues génériques de formulaires d'éditions s'appuyant sur des classes.   </td>
- </tr>
- </tbody>
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis:</th>
+ <td>
+ Avoir terminé les formations précédentes, y compris
+ <a href="/fr/docs/Learn/Server-side/Django/authentication_and_sessions"
+ >Django Tutorial Part 8: User authentication and permissions</a
+ >.
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Objectifs:</th>
+ <td>
+ Comprendre comment écrire des formulaires pour récupérer des
+ informations de la part des utilisateurs et mettre à jour la base de
+ données. Comprendre commment il est possible de simplifier grandement la
+ création de formulaires si l 'on travaille avec un seul modèle en
+ utilisant les vues génériques de formulaires d'éditions s'appuyant sur
+ des classes.   
+ </td>
+ </tr>
+ </tbody>
</table>
-<h2 id="Vue_densemble">Vue d'ensemble</h2>
+## Vue d'ensemble
+
+Un [formulaire HTML](/fr/docs/Web/Guide/HTML/Forms) regroupe au moins un champ remplissable et des composants élémentaires d'interface web.  Il peut être utilisé pour réunir des saisies de la part des utilisateurs avant envoi vers un serveur. Les formulaires sont souples: ils s'adaptent à plusieurs modes de saisie. En effet, Il existe des composants élementaires d'interfaces graphique pour des  modes de saisie non contrainte avec une zone de saisie de texte, ou resteinte au type date avec un date picker,  la saisie d'un variable optionnelle via une boîte à cocher,  d'un choix à faire parmi plusieurs valeurs possibles avec les boutons radio etc... . Les formulaires permettent de partager des informations avec le serveur de manière relativement sécurisée  car ils permettent d'envoyer des requêtes de  type `POST` protégeant de la falsification des requêtes inter-site.
+
+Bien que nous n'ayons pas encore créé de formulaire au cours de cette formation, nous en avons déjà rencontré sur l'interface d'administration Django Admin — par exemple la capture d'écran ci-dessous montre un formulaire d'édition  de l'un de nos modèle de [Book](/fr/docs/Learn/Server-side/Django/Models)  (livre), comprenant des composants élémentaires d'interface graphique de chois de valeur parmi une liste proposée,  et des zones des saisie de texte.
+
+![Admin Site - Book Add](admin_book_add.png)
+
+Travailler avec des formulaires peut s'avérer compliqué ! Les développeurs doivent non seulement écrire le code  HTML pour le formulaire, mais aussi vérifier et corriger sur le serveur les données saisies (et éventuellement aussi dans le navigateur), renvoyer le formulaire avec des messages d'erreur pour informer les usagers de tout champ invalide, prendre en charge les données quand elles passent l'étape de vérification, et finalement renvoyer une information à l'utilisateur d'une manière ou d'une autre pour indiquer ce succès. Les formulaires sous Django enlèvent beaucoup de travail à chacune de ces étapes, grâce à un cadriciel qui permet de déclarer des formulaires et leurs champs à travers un langage de programmation, puis d'utiliser ces objets non seulement pour générer le code HTML, mais aussi une grosse partie de la vérification des données et du retour d'information à l'utilisateur.
+
+Dans cette formation, nous allons vous montrer quelque-unes des manièrs de créer et de travailler avec les formulaires, et en particulier, comment les vues sur les formulaires génériques d'édition peuvent réduire significativement la quantité de travail à fournir pour créer les formulaires de manipulation de vos modèles. En chemin nous allons étendre notre application *LocalLibrary* en ajoutant un formulaire permettant aux bibliothécaires de prolonger le prêt de libres, et nous allons créer des pages pour créer, modifier et supprimer des livres et des auteurs (reproduisant une version basique du formulaire ci-dessus pour éditer des livres. )
+
+## Formulaires HTML
-<p>Un <a href="/fr/docs/Web/Guide/HTML/Forms">formulaire HTML</a> regroupe au moins un champ remplissable et des composants élémentaires d'interface web.  Il peut être utilisé pour réunir des saisies de la part des utilisateurs avant envoi vers un serveur. Les formulaires sont souples: ils s'adaptent à plusieurs modes de saisie. En effet, Il existe des composants élementaires d'interfaces graphique pour des  modes de saisie non contrainte avec une zone de saisie de texte, ou resteinte au type date avec un date picker,  la saisie d'un variable optionnelle via une boîte à cocher,  d'un choix à faire parmi plusieurs valeurs possibles avec les boutons radio etc... . Les formulaires permettent de partager des informations avec le serveur de manière relativement sécurisée  car ils permettent d'envoyer des requêtes de  type <code>POST</code> protégeant de la falsification des requêtes inter-site.</p>
+D'abord, un premier aperçu des formulaires HTML ([HTML Forms](/fr/docs/Learn/HTML/Forms)). Soit un formulaire HTML simple, composé d'un unique champ de saisie texte , présent pour y entrer le nom d'une "équipe" quelconque, et son sa description dans l'étiquette associée :
-<p>Bien que nous n'ayons pas encore créé de formulaire au cours de cette formation, nous en avons déjà rencontré sur l'interface d'administration Django Admin — par exemple la capture d'écran ci-dessous montre un formulaire d'édition  de l'un de nos modèle de <a href="/fr/docs/Learn/Server-side/Django/Models">Book</a>  (livre), comprenant des composants élémentaires d'interface graphique de chois de valeur parmi une liste proposée,  et des zones des saisie de texte.</p>
+![Simple name field example in HTML form](form_example_name_field.png)
-<p><img alt="Admin Site - Book Add" src="admin_book_add.png"></p>
+Le formulaire est défini en HTML comme une collection d'éléments enfermés entre deux balises \<form> ... \</form> contenant au moins une balise \<input> dont la valeur d'attribut 'type' doit valoir "submit":
-<p>Travailler avec des formulaires peut s'avérer compliqué ! Les développeurs doivent non seulement écrire le code  HTML pour le formulaire, mais aussi vérifier et corriger sur le serveur les données saisies (et éventuellement aussi dans le navigateur), renvoyer le formulaire avec des messages d'erreur pour informer les usagers de tout champ invalide, prendre en charge les données quand elles passent l'étape de vérification, et finalement renvoyer une information à l'utilisateur d'une manière ou d'une autre pour indiquer ce succès. Les formulaires sous Django enlèvent beaucoup de travail à chacune de ces étapes, grâce à un cadriciel qui permet de déclarer des formulaires et leurs champs à travers un langage de programmation, puis d'utiliser ces objets non seulement pour générer le code HTML, mais aussi une grosse partie de la vérification des données et du retour d'information à l'utilisateur.</p>
+```html
+<form action="/team_name_url/" method="post">
+    <label for="team_name">Enter name: </label>
+    <input id="team_name" type="text" name="name_field" value="Default name for team.">
+    <input type="submit" value="OK">
+</form>
+```
-<p>Dans cette formation, nous allons vous montrer quelque-unes des manièrs de créer et de travailler avec les formulaires, et en particulier, comment les vues sur les formulaires génériques d'édition peuvent réduire significativement la quantité de travail à fournir pour créer les formulaires de manipulation de vos modèles. En chemin nous allons étendre notre application <em>LocalLibrary</em> en ajoutant un formulaire permettant aux bibliothécaires de prolonger le prêt de libres, et nous allons créer des pages pour créer, modifier et supprimer des livres et des auteurs (reproduisant une version basique du formulaire ci-dessus pour éditer des livres. )</p>
+Bien qu'ici nous n'ayons qu'un champ de saisie texte destiné à recevoir le nom d'équipe, une formulaire _pourrait_ avoir un nombre quelconque d'autres champs de saisie et leurs étiquettes de description associées. La valeur de l'attribut 'type' définit la sorte de composant élementaire d'interface graphique  affichée. Les attributs 'id' et 'name' permettent d'identifier le champ en JavaScript/CSS/HTML alors que l'attribut 'value' définit la valeur initiale du champ lorsqu'il est affiché pour la première fois. La description associée  est déclarée par la balise \<label> (voir "Enter Name" au dessus) , avec un attribut 'for' devant contenir la valeur de l'attribut 'id' du champ imput à laquelle on souhaite l'associer.
-<h2 id="Formulaires_HTML">Formulaires HTML</h2>
+La balise `<input>` dont l'attribut `'type'` vaut `submit` sera affichée (par défaut) comme un bouton  qui peut être cliqué par l'utilisateur pour envoyer vers le serveur les données figurant dans tous les autres éléments de formulaire \<input> (dans le cas présent, la valeur actuelle de `'team name'`. Les attributs de formulaire déterminent d'une part la méthode HTTP (attribut method) utilisée pour envoyer les donnnées et d'autre part la destination des données sur le serveur (attribut `action `):
-<p>D'abord, un premier aperçu des formulaires HTML (<a href="/fr/docs/Learn/HTML/Forms">HTML Forms</a>). Soit un formulaire HTML simple, composé d'un unique champ de saisie texte , présent pour y entrer le nom d'une "équipe" quelconque, et son sa description dans l'étiquette associée :</p>
+- `action` :  Il s'agit de la destination (ressource ou URL) où sont envoyées les données lorsque le formulaire est soumis.  Si la valeur de cet attribut n'est pas initialisée (ou la chaine vide est affectée à cet attribut),  alors le formulaire sera renvoyé à l' URL de la page courante.
+- `method` : La méthode HTTP utilisée pour envoyer les données: *post* ou *get*.
-<p><img alt="Simple name field example in HTML form" src="form_example_name_field.png"></p>
+ - La méthode `POST` devrait toujours être utilisée si l'envoi de la donnée va provoquer un changement dan la base de données du serveur, car il peut être rendu plus résistant aux attaques par falsification de requête inter-site (CSRF).
+ - La méthode GET ne devrait être utilisée que pour les formulaires ne changeant pas les données utilisateur (p.ex. un formulaire de recherche) . Elle est recommandée lorsque vous souhaitez pouvoir partager l'URL ou la conserver dans vos favoris.  T
-<p>Le formulaire est défini en HTML comme une collection d'éléments enfermés entre deux balises &lt;form&gt; ... &lt;/form&gt; contenant au moins une balise &lt;input&gt; dont la valeur d'attribut 'type' doit valoir "submit":</p>
+Le rôle du serveur est d'abord de fournir le formulaire sous sa forme initiale  — c'est à dire une série de champs soit vides, soit préremplis avec des valeurs initiales. Après l'impulsion de l'utilisateur sur le bouton submit, le seurveur va recevoir les données du formulaire avec les valeurs saisies dans le navigateur, et va devoir vérifier ces données. Si le formulaire contient des données invalides, le seurveur devrait afficher le formulaire de nouveau, cette fois ci avec les données utilisateur entrées dans les champs "valides" et des messages pour décrire le problème pour les champs invalides. Dès que le serveur reçoit une requête dont tous les données de champs sont valides, il peut effectuer les actions appropriées ( c'est à dire sauver les données, renvoyer le résultat d'une recherche, téléverser un fichier, etc...)  et ensuite notifier l'utilisateur .
-<pre class="brush: html">&lt;form action="/team_name_url/" method="post"&gt;
-    &lt;label for="team_name"&gt;Enter name: &lt;/label&gt;
-    &lt;input id="team_name" type="text" name="name_field" value="Default name for team."&gt;
-    &lt;input type="submit" value="OK"&gt;
-&lt;/form&gt;
-</pre>
+Comme vous pouvez l'imaginer, créer le code HTML, vérifier les données envoyées, réafficher les données entrées avec l'adjonction de rapports sur les erreurs, effectuer les opérations désirées sur les données valides peut représenter pas mal d'efforts de réflexion et d'essais erreur. Django rend cela bien plus facile, en enlevant la nécessité de concevoir une partie de ce code pénible et répétitif!
-<p>Bien qu'ici nous n'ayons qu'un champ de saisie texte destiné à recevoir le nom d'équipe, une formulaire <em>pourrait</em> avoir un nombre quelconque d'autres champs de saisie et leurs étiquettes de description associées. La valeur de l'attribut 'type' définit la sorte de composant élementaire d'interface graphique  affichée. Les attributs 'id' et 'name' permettent d'identifier le champ en JavaScript/CSS/HTML alors que l'attribut 'value' définit la valeur initiale du champ lorsqu'il est affiché pour la première fois. La description associée  est déclarée par la balise &lt;label&gt; (voir "Enter Name" au dessus) , avec un attribut 'for' devant contenir la valeur de l'attribut 'id' du champ imput à laquelle on souhaite l'associer.  </p>
+## Les étapes de gestion d'un formulaire avec Django
-<p>La balise <code>&lt;input&gt;</code> dont l'attribut <code>'type'</code> vaut <code>submit</code> sera affichée (par défaut) comme un bouton  qui peut être cliqué par l'utilisateur pour envoyer vers le serveur les données figurant dans tous les autres éléments de formulaire &lt;input&gt; (dans le cas présent, la valeur actuelle de <code>'team name'</code>. Les attributs de formulaire déterminent d'une part la méthode HTTP (attribut method) utilisée pour envoyer les donnnées et d'autre part la destination des données sur le serveur (attribut <code>action </code>):</p>
+Django gère un formulaire en utilisant les mêmes techniques qu'évoquées lors des formations précédentes (pour afficher des informations à propos de nos modèles): La vue reçoit une requête, exécute toute acion nécessaire, incluant la lecture de données depuis les modèles, puis génère une page HTML (à partir d'un squelette à qui nous transmettons un _contexte_ contenant les données à afficher\* \*). Ce qui rend les choses plus compliquées, c'est que le serveur a aussi besoin d'être capable de traiter les données fournies par l'utilisateur (pas seulement le contexte) et doit pouvoir réafficher les pages s'il y a une quelconque erreur.
-<ul>
- <li><code>action</code> :  Il s'agit de la destination (ressource ou URL) où sont envoyées les données lorsque le formulaire est soumis.  Si la valeur de cet attribut n'est pas initialisée (ou la chaine vide est affectée à cet attribut),  alors le formulaire sera renvoyé à l' URL de la page courante.</li>
- <li><code>method</code> : La méthode HTTP utilisée pour envoyer les données: <em>post</em> ou <em>get</em>.
- <ul>
- <li>La méthode <code>POST</code> devrait toujours être utilisée si l'envoi de la donnée va provoquer un changement dan la base de données du serveur, car il peut être rendu plus résistant aux attaques par falsification de requête inter-site (CSRF). </li>
- <li>La méthode GET ne devrait être utilisée que pour les formulaires ne changeant pas les données utilisateur (p.ex. un formulaire de recherche) . Elle est recommandée lorsque vous souhaitez pouvoir partager l'URL ou la conserver dans vos favoris.  T</li>
- </ul>
- </li>
-</ul>
+Voici ci-dessous un diagramme représentant les étapes de gestion d'un formulaire de requêtes, commencant par la demande par le navigateur d'une page, dont le code HTML se trouve contenir un formulaire (en vert).
-<p>Le rôle du serveur est d'abord de fournir le formulaire sous sa forme initiale  — c'est à dire une série de champs soit vides, soit préremplis avec des valeurs initiales. Après l'impulsion de l'utilisateur sur le bouton submit, le seurveur va recevoir les données du formulaire avec les valeurs saisies dans le navigateur, et va devoir vérifier ces données. Si le formulaire contient des données invalides, le seurveur devrait afficher le formulaire de nouveau, cette fois ci avec les données utilisateur entrées dans les champs "valides" et des messages pour décrire le problème pour les champs invalides. Dès que le serveur reçoit une requête dont tous les données de champs sont valides, il peut effectuer les actions appropriées ( c'est à dire sauver les données, renvoyer le résultat d'une recherche, téléverser un fichier, etc...)  et ensuite notifier l'utilisateur .  </p>
+![Updated form handling process doc.](Form%20Handling%20-%20Standard.png)
-<p>Comme vous pouvez l'imaginer, créer le code HTML, vérifier les données envoyées, réafficher les données entrées avec l'adjonction de rapports sur les erreurs, effectuer les opérations désirées sur les données valides peut représenter pas mal d'efforts de réflexion et d'essais erreur. Django rend cela bien plus facile, en enlevant la nécessité de concevoir une partie de ce code pénible et répétitif!</p>
+En se basant sur la lecture du diagramme ci-dessus, les tâches principales dont s'aquitte Django à l'occasion de la gestion d'un formulaire sont :
-<h2 id="Les_étapes_de_gestion_dun_formulaire_avec_Django">Les étapes de gestion d'un formulaire avec Django</h2>
+1. Afficher le formulaire sous sa forme par défaut la première fois où il est demandé par l'utilisateur.
-<p>Django gère un formulaire en utilisant les mêmes techniques qu'évoquées lors des formations précédentes (pour afficher des informations à propos de nos modèles): La vue reçoit une requête, exécute toute acion nécessaire, incluant la lecture de données depuis les modèles, puis génère une page HTML (à partir d'un squelette à qui nous transmettons un <em>contexte </em>contenant les données à afficher<em> </em>). Ce qui rend les choses plus compliquées, c'est que le serveur a aussi besoin d'être capable de traiter les données fournies par l'utilisateur (pas seulement le contexte) et doit pouvoir réafficher les pages s'il y a une quelconque erreur.</p>
+ - Le formulaire peut contenir des champs vides (par exemple si vous créez un nouvel enregistrement ) ou peut être prérempli de valeurs initiales (par exemple si vous modifiez les valeurs d'un enregistrement existant, ou que ces champs ont des valeurs initiales utiles ).
+ - Le formulaire est qualifié à cette étape de formulaire libre, parce qu'il n'est associé à aucune donnée entré par l'utilisateur (bien qu'il puisse avoir des valeurs initiales) .
-<p>Voici ci-dessous un diagramme représentant les étapes de gestion d'un formulaire de requêtes, commencant par la demande par le navigateur d'une page, dont le code HTML se trouve contenir un formulaire (en vert).</p>
+2. Recevoir des données d'une requete d'envoi de données et les lier au formulaire.
-<p><img alt="Updated form handling process doc." src="Form%20Handling%20-%20Standard.png"></p>
+ - Lier les données au formulaire signifie que les données entrées par l'utilisateur, ainsi que les erreurs éventuelles sont accessibles lorsque nous avons besoin de réafficher le formulaire.
-<p>En se basant sur la lecture du diagramme ci-dessus, les tâches principales dont s'aquitte Django à l'occasion de la gestion d'un formulaire sont : </p>
+3. Nettoyer et valider les données
-<ol>
- <li>Afficher le formulaire sous sa forme par défaut la première fois où il est demandé par l'utilisateur.
- <ul>
- <li>Le formulaire peut contenir des champs vides (par exemple si vous créez un nouvel enregistrement ) ou peut être prérempli de valeurs initiales (par exemple si vous modifiez les valeurs d'un enregistrement existant, ou que ces champs ont des valeurs initiales utiles ).</li>
- <li>Le formulaire est qualifié à cette étape de formulaire libre, parce qu'il n'est associé à aucune donnée entré par l'utilisateur (bien qu'il puisse avoir des valeurs initiales) . </li>
- </ul>
- </li>
- <li>Recevoir des données d'une requete d'envoi de données et les lier au formulaire.
- <ul>
- <li>Lier les données au formulaire signifie que les données entrées par l'utilisateur, ainsi que les erreurs éventuelles sont accessibles lorsque nous avons besoin de réafficher le formulaire. </li>
- </ul>
- </li>
- <li>Nettoyer et valider les données
- <ul>
- <li>Le nettoyage de données consiste à désinfecter la saisie (par exemple en supprimant les caractères non valides, et qui pourraient être utilisés pour envoyer du contenu malveillant au serveur.) et à convertir ces données en types Python cohérents. </li>
- <li>La validation vérifie que les valeurs envoyées sont appropriées au champ (par exemple dans le bon intervalle de dates, ni trop long ni trop court, etc.) </li>
- </ul>
- </li>
- <li>Si une donnée n'est pas valide, ré-affiche le formulaire, cette fois-ci avec les données déjà saisies par l'utilisateur et les messages d'erreur pour les champs en erreur.</li>
- <li>Si toutes les données sont conformes, effectue les actions demandées (e.g. sauvegarde les données, envoyer un mail, renvoie le résultat d'une recherche, télécharge un fichier etc.)</li>
- <li>Une fois toutes ces actions accomplies, redirige l'utilisateur vers une autre page.</li>
-</ol>
+ - Le nettoyage de données consiste à désinfecter la saisie (par exemple en supprimant les caractères non valides, et qui pourraient être utilisés pour envoyer du contenu malveillant au serveur.) et à convertir ces données en types Python cohérents.
+ - La validation vérifie que les valeurs envoyées sont appropriées au champ (par exemple dans le bon intervalle de dates, ni trop long ni trop court, etc.)
-<p>Django fournit une multitude d'outils et de méthodes pour vous assister dans les tâches mentionnées ci-dessus. Parmi eux la plus importante, la classe <code>Form</code>, qui simplifie à la fois la production de formulaire HTML mais aussi la validation de donnée. In the next section we describe how forms work using the practical example of a page to allow librarians to renew books.</p>
+4. Si une donnée n'est pas valide, ré-affiche le formulaire, cette fois-ci avec les données déjà saisies par l'utilisateur et les messages d'erreur pour les champs en erreur.
+5. Si toutes les données sont conformes, effectue les actions demandées (e.g. sauvegarde les données, envoyer un mail, renvoie le résultat d'une recherche, télécharge un fichier etc.)
+6. Une fois toutes ces actions accomplies, redirige l'utilisateur vers une autre page.
-<div class="note">
-<p><strong>Note :</strong> Comprendre l'utilisation de <code>Form</code> vous aidera quand nous parlerons des classes de formulaires de Django plus complexes. </p>
-</div>
+Django fournit une multitude d'outils et de méthodes pour vous assister dans les tâches mentionnées ci-dessus. Parmi eux la plus importante, la classe `Form`, qui simplifie à la fois la production de formulaire HTML mais aussi la validation de donnée. In the next section we describe how forms work using the practical example of a page to allow librarians to renew books.
-<h2 id="Formulaire_de_renouvellement_de_livre_par_lutilisation_de_vue_Form">Formulaire de renouvellement de livre par l'utilisation de vue Form</h2>
+> **Note :** Comprendre l'utilisation de `Form` vous aidera quand nous parlerons des classes de formulaires de Django plus complexes.
-<p>Nous allons maintenant créer une page qui permettra aux bibliothécaires de renouveler les livres empruntés (les rendre disponible à nouveau). Pour cela nous allons créer un formulaire qui permet aux utilisateurs de saisir une valeur de type Date. Considérons le champ avec une valeur initiale égale à la date du jour plus 3 semaines (la période normale d'emprunt d'un livre), et ajouter une validation pour s'assurer que le bibliothécaire ne peut pas saisir une date dans le passé ou une date trop éloignée dans le futur. Quand une date valide a été entrée, nous l'enregistrons dans le champ <code>BookInstance.due_back</code> de l'enregistrement courant.</p>
+## Formulaire de renouvellement de livre par l'utilisation de vue Form
-<p>L'exemple va utiliser une vue basée sur une fonction et une classe <code>Form</code>. Les sections suivantes expliquent comment les formulaires fonctionnent, et les changements que vous devez faire à notre projet en cours <em>LocalLibrary</em>.</p>
+Nous allons maintenant créer une page qui permettra aux bibliothécaires de renouveler les livres empruntés (les rendre disponible à nouveau). Pour cela nous allons créer un formulaire qui permet aux utilisateurs de saisir une valeur de type Date. Considérons le champ avec une valeur initiale égale à la date du jour plus 3 semaines (la période normale d'emprunt d'un livre), et ajouter une validation pour s'assurer que le bibliothécaire ne peut pas saisir une date dans le passé ou une date trop éloignée dans le futur. Quand une date valide a été entrée, nous l'enregistrons dans le champ `BookInstance.due_back` de l'enregistrement courant.
-<h3 id="Formulaire">Formulaire</h3>
+L'exemple va utiliser une vue basée sur une fonction et une classe `Form`. Les sections suivantes expliquent comment les formulaires fonctionnent, et les changements que vous devez faire à notre projet en cours _LocalLibrary_.
-<p>La classe <code>Form</code> est le cœur du système de gestion des formulaires de Django. Elle spécifie les champs présents dans le formulaire, affiche les widgets, les labels, les valeurs initiales, les valeurs valides et (après validation) les messages d'erreur associés aux champs invalides. Cette classe fournit également des méthodes pour se restituer elle-même dans les templates en utilisant des formats prédéfinis (tables, listes etc.) ou pour obtenir la valeur de chaque élément de formulaire (permettant un rendu manuel fin).</p>
+### Formulaire
-<h4 id="Déclarer_un_formulaire">Déclarer un formulaire</h4>
+La classe `Form` est le cœur du système de gestion des formulaires de Django. Elle spécifie les champs présents dans le formulaire, affiche les widgets, les labels, les valeurs initiales, les valeurs valides et (après validation) les messages d'erreur associés aux champs invalides. Cette classe fournit également des méthodes pour se restituer elle-même dans les templates en utilisant des formats prédéfinis (tables, listes etc.) ou pour obtenir la valeur de chaque élément de formulaire (permettant un rendu manuel fin).
-<p>La syntaxe de déclaration pour un <code>Form</code> est très semblable à celle utilisée pour déclarer un <code>Model</code>, et partage les mêmes types de champs (et des paramètres similaires). Cela est logique, puisque dans les deux cas nous avons besoin de nous assurer que chaque champ gère le bon type de donnée, est contraint lors de la validation des données, et a une description pour l'affichage/la documentation.</p>
+#### Déclarer un formulaire
-<p>Les données de formulaire sont stockées dans un fichier application forms.py, à l'intérieur du répertoire de l'application. Créez et ouvrez le fichier <strong>locallibrary/catalog/forms.py</strong>. Pour créer un <code>Form</code>, nous importons la bibliothèque <code>forms</code>, dérivons une classe de la classe <code>Form</code>, et déclarons les champs du formulaire. Une classe très basique de formulaire pour notre formulaire de renouvellement de livre dans notre bibliothèque est montrée ci-dessous :</p>
+La syntaxe de déclaration pour un `Form` est très semblable à celle utilisée pour déclarer un `Model`, et partage les mêmes types de champs (et des paramètres similaires). Cela est logique, puisque dans les deux cas nous avons besoin de nous assurer que chaque champ gère le bon type de donnée, est contraint lors de la validation des données, et a une description pour l'affichage/la documentation.
-<pre class="brush: python">from django import forms
+Les données de formulaire sont stockées dans un fichier application forms.py, à l'intérieur du répertoire de l'application. Créez et ouvrez le fichier **locallibrary/catalog/forms.py**. Pour créer un `Form`, nous importons la bibliothèque `forms`, dérivons une classe de la classe `Form`, et déclarons les champs du formulaire. Une classe très basique de formulaire pour notre formulaire de renouvellement de livre dans notre bibliothèque est montrée ci-dessous :
+
+```python
+from django import forms
class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
-</pre>
+```
-<h4 id="Champs_de_formulaire">Champs de formulaire</h4>
+#### Champs de formulaire
-<p>Dans ce cas, nous avons un unique champ <code><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datefield">DateField</a></code> pour entrer la date du renouvellement, qui sera rendue en HTML avec une valeur vide, le label par défaut "<em>Renewal date:</em>", et un texte utilitaire indiquant comment s'en servir : "<em>Enter a date between now and 4 weeks (default 3 weeks).</em>" Comme aucun des autres arguments optionnels ne sont spécifiés, le champ acceptera des dates en utilisant les <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#django.forms.DateField.input_formats">input_formats</a> suivants : YYYY-MM-DD (2016-11-06), MM/DD/YYYY (02/26/2016), MM/DD/YY (10/25/16), et sera rendu en utilisant le <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#widget">widget</a> par défaut : <a href="https://docs.djangoproject.com/en/1.10/ref/forms/widgets/#django.forms.DateInput">DateInput</a>.</p>
+Dans ce cas, nous avons un unique champ [`DateField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datefield) pour entrer la date du renouvellement, qui sera rendue en HTML avec une valeur vide, le label par défaut "_Renewal date:_", et un texte utilitaire indiquant comment s'en servir : "_Enter a date between now and 4 weeks (default 3 weeks)._" Comme aucun des autres arguments optionnels ne sont spécifiés, le champ acceptera des dates en utilisant les [input_formats](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#django.forms.DateField.input_formats) suivants : YYYY-MM-DD (2016-11-06), MM/DD/YYYY (02/26/2016), MM/DD/YY (10/25/16), et sera rendu en utilisant le [widget](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#widget) par défaut : [DateInput](https://docs.djangoproject.com/en/1.10/ref/forms/widgets/#django.forms.DateInput).
-<p>Il y a beaucoup d'autres types de champs, que vous reconnaîtrez sans peine en raison de leur ressemblance avec les classes de champs équivalentes pour les modèles : <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#booleanfield"><code>BooleanField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#charfield"><code>CharField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#choicefield"><code>ChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#typedchoicefield"><code>TypedChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datefield"><code>DateField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datetimefield"><code>DateTimeField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#decimalfield"><code>DecimalField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#durationfield"><code>DurationField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#emailfield"><code>EmailField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#filefield"><code>FileField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#filepathfield"><code>FilePathField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#floatfield"><code>FloatField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#imagefield"><code>ImageField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#integerfield"><code>IntegerField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#genericipaddressfield"><code>GenericIPAddressField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#multiplechoicefield"><code>MultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#typedmultiplechoicefield"><code>TypedMultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#nullbooleanfield"><code>NullBooleanField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#regexfield"><code>RegexField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#slugfield"><code>SlugField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#timefield"><code>TimeField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#urlfield"><code>URLField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#uuidfield"><code>UUIDField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#combofield"><code>ComboField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#multivaluefield"><code>MultiValueField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#splitdatetimefield"><code>SplitDateTimeField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#modelmultiplechoicefield"><code>ModelMultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#modelchoicefield"><code>ModelChoiceField</code></a>​​​​.</p>
+Il y a beaucoup d'autres types de champs, que vous reconnaîtrez sans peine en raison de leur ressemblance avec les classes de champs équivalentes pour les modèles : [`BooleanField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#booleanfield), [`CharField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#charfield), [`ChoiceField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#choicefield), [`TypedChoiceField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#typedchoicefield), [`DateField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datefield), [`DateTimeField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datetimefield), [`DecimalField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#decimalfield), [`DurationField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#durationfield), [`EmailField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#emailfield), [`FileField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#filefield), [`FilePathField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#filepathfield), [`FloatField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#floatfield), [`ImageField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#imagefield), [`IntegerField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#integerfield), [`GenericIPAddressField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#genericipaddressfield), [`MultipleChoiceField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#multiplechoicefield), [`TypedMultipleChoiceField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#typedmultiplechoicefield), [`NullBooleanField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#nullbooleanfield), [`RegexField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#regexfield), [`SlugField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#slugfield), [`TimeField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#timefield), [`URLField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#urlfield), [`UUIDField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#uuidfield), [`ComboField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#combofield), [`MultiValueField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#multivaluefield), [`SplitDateTimeField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#splitdatetimefield), [`ModelMultipleChoiceField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#modelmultiplechoicefield), [`ModelChoiceField`](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#modelchoicefield)​​​​.
-<p>Les arguments communs de la plupart des champs sont listés ci-dessous (ils ont les valeurs les plus communes par défaut) :</p>
+Les arguments communs de la plupart des champs sont listés ci-dessous (ils ont les valeurs les plus communes par défaut) :
-<ul>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#required">required</a>: Si <code>True</code>, le champ ne peut être laissé vide ou recevoir une valeur <code>None</code>. Les champs sont requis par défaut, aussi devez-vous préciser <code>required=False</code> pour autoriser des valeurs vides dans le formulaire.</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label">label</a>: Le label à utiliser au moment de rendre le champ en HTML. Si <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label">label</a> n'est pas précisé, alors Django en créera un à partir du nom du champ concerné, en mettant en majuscule la première lettre et en remplaçant les tirets bas par des espaces (p. ex. <em>Renewal date</em>).</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label-suffix">label_suffix</a>: Par défaut, un double point est affiché après le label (p. ex. Renewal date<strong>:</strong>). Cet argument vous permet de préciser un suffixe différent contenant un ou plusieurs autres caractère(s).</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#initial">initial</a>: La valeur intiale pour le champ lorsque le formulaire est affiché.</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#widget">widget</a>: Le widget d'affichage à utiliser.</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#help-text">help_text</a> (comme dans l'exemple ci-dessus): Un texte supplémentaire qui peut être affiché dans les formulaires pour expliquer comment utiliser le champ.</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#error-messages">error_messages</a>: Une liste des messages d'erreur pour le champ. Vous pouvez remplacer les messages par défaut par vos propres messages si besoin.</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#validators">validators</a>: Une liste de fonctions qui seront appelées quand le champ sera validé.</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#localize">localize</a>: Autorise la forme locale des données de formulaire (voyez le lien pour information).</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#disabled">disabled</a>: Si <code>True</code>, le champ est affiché, mais sa valeur ne peut être modifiée. <code>False</code> par défaut.</li>
-</ul>
+- [required](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#required): Si `True`, le champ ne peut être laissé vide ou recevoir une valeur `None`. Les champs sont requis par défaut, aussi devez-vous préciser `required=False` pour autoriser des valeurs vides dans le formulaire.
+- [label](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label): Le label à utiliser au moment de rendre le champ en HTML. Si [label](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label) n'est pas précisé, alors Django en créera un à partir du nom du champ concerné, en mettant en majuscule la première lettre et en remplaçant les tirets bas par des espaces (p. ex. _Renewal date_).
+- [label_suffix](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label-suffix): Par défaut, un double point est affiché après le label (p. ex. Renewal date**:**). Cet argument vous permet de préciser un suffixe différent contenant un ou plusieurs autres caractère(s).
+- [initial](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#initial): La valeur intiale pour le champ lorsque le formulaire est affiché.
+- [widget](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#widget): Le widget d'affichage à utiliser.
+- [help_text](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#help-text) (comme dans l'exemple ci-dessus): Un texte supplémentaire qui peut être affiché dans les formulaires pour expliquer comment utiliser le champ.
+- [error_messages](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#error-messages): Une liste des messages d'erreur pour le champ. Vous pouvez remplacer les messages par défaut par vos propres messages si besoin.
+- [validators](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#validators): Une liste de fonctions qui seront appelées quand le champ sera validé.
+- [localize](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#localize): Autorise la forme locale des données de formulaire (voyez le lien pour information).
+- [disabled](https://docs.djangoproject.com/en/1.10/ref/forms/fields/#disabled): Si `True`, le champ est affiché, mais sa valeur ne peut être modifiée. `False` par défaut.
-<h4 id="Validation">Validation</h4>
+#### Validation
-<p>Django fournit un grand nombre d'endroits pour valider vos données. La façon la plus simple de valider un champ unique est de remplacer la méthode <code>clean_<strong>&lt;fieldname&gt;</strong>()</code> pour le champ à vérifier. Ainsi, par exemple, nous pouvons vérifier que les valeurs entrées pour le champ <code>renewal_date</code> sont entre maintenant et dans 4 semaines, en implémentant la méthode <code>clean_<strong>renewal_date</strong>()</code> comme montré ci-après.</p>
+Django fournit un grand nombre d'endroits pour valider vos données. La façon la plus simple de valider un champ unique est de remplacer la méthode `clean_<fieldname>()` pour le champ à vérifier. Ainsi, par exemple, nous pouvons vérifier que les valeurs entrées pour le champ `renewal_date` sont entre maintenant et dans 4 semaines, en implémentant la méthode `clean_renewal_date()` comme montré ci-après.
-<p>Mettez à jour votre fichier forms.py, de telle sorte qu'il ressemble à cela :</p>
+Mettez à jour votre fichier forms.py, de telle sorte qu'il ressemble à cela :
-<pre class="brush: python">from django import forms
+```python
+from django import forms
-<strong>from django.core.exceptions import ValidationError
+from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
import datetime #for checking renewal date range.
-</strong>
+
class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
-<strong>    def clean_renewal_date(self):
+    def clean_renewal_date(self):
        data = self.cleaned_data['renewal_date']
        #Check date is not in past.
-        if data &lt; datetime.date.today():
+        if data < datetime.date.today():
            raise ValidationError(_('Invalid date - renewal in past'))
        #Check date is in range librarian allowed to change (+4 weeks).
-        if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+        if data > datetime.date.today() + datetime.timedelta(weeks=4):
            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
        # Remember to always return the cleaned data.
-        return data</strong></pre>
+        return data
+```
-<p>Il y a deux choses importantes à noter. La première est que nous accédons à nos données en utilisant <code>self.cleaned_data['renewal_date']</code>, et que nous retournons ces données, que nous les ayons changées ou non, à la fin de la fonction. Cette étape nous donne des données "nettoyées", purgées de valeurs potentiellement dangereuses en utilisant les validateurs par défaut, et converties en type standard correct pour les données considérées (dans ce cas un objet Python <code>datetime.datetime</code>).</p>
+Il y a deux choses importantes à noter. La première est que nous accédons à nos données en utilisant `self.cleaned_data['renewal_date']`, et que nous retournons ces données, que nous les ayons changées ou non, à la fin de la fonction. Cette étape nous donne des données "nettoyées", purgées de valeurs potentiellement dangereuses en utilisant les validateurs par défaut, et converties en type standard correct pour les données considérées (dans ce cas un objet Python `datetime.datetime`).
-<p>Le deuxième point est que, si une valeur tombe en dehors de l'intervalle que nous avons autorisé, nous levons une <code>ValidationError</code>, en spécifiant le texte d'erreur que nous voulons afficher dans la zone du formulaire prévue pour le cas où l'utilisateur entre une valeur incorrecte. L'exemple ci-dessus enveloppe aussi ce texte dans <code>ugettext_lazy()</code> (importée comme <code>_()</code>), une des <a class="external" href="https://docs.djangoproject.com/en/2.1/topics/i18n/translation/" rel="noopener">fonctions de traduction Django</a>, ce qui est une bonne pratique si vous voulez traduire votre site plus tard.</p>
+Le deuxième point est que, si une valeur tombe en dehors de l'intervalle que nous avons autorisé, nous levons une `ValidationError`, en spécifiant le texte d'erreur que nous voulons afficher dans la zone du formulaire prévue pour le cas où l'utilisateur entre une valeur incorrecte. L'exemple ci-dessus enveloppe aussi ce texte dans `ugettext_lazy()` (importée comme `_()`), une des [fonctions de traduction Django](https://docs.djangoproject.com/en/2.1/topics/i18n/translation/), ce qui est une bonne pratique si vous voulez traduire votre site plus tard.
-<div class="note">
-<p><strong>Note :</strong> Il y a un grand nombre d'autres méthodes et exemples au sujet de la validation des formulaires dans <a class="external" href="https://docs.djangoproject.com/en/2.1/ref/forms/validation/" rel="noopener">Form and field validation</a> (Django Docs). Par exemple, au cas où vous avez plusieurs champs dépendants les uns des autres, vous pouvez réécrire la fonction <a class="external" href="https://docs.djangoproject.com/en/2.1/ref/forms/api/#django.forms.Form.clean" rel="noopener">Form.clean()</a>, et lever de nouveau une <code>ValidationError</code>.</p>
-</div>
+> **Note :** Il y a un grand nombre d'autres méthodes et exemples au sujet de la validation des formulaires dans [Form and field validation](https://docs.djangoproject.com/en/2.1/ref/forms/validation/) (Django Docs). Par exemple, au cas où vous avez plusieurs champs dépendants les uns des autres, vous pouvez réécrire la fonction [Form.clean()](https://docs.djangoproject.com/en/2.1/ref/forms/api/#django.forms.Form.clean), et lever de nouveau une `ValidationError`.
-<p>C'est tout ce dont nous avons besoin pour notre formulaire dans cet exemple.</p>
+C'est tout ce dont nous avons besoin pour notre formulaire dans cet exemple.
-<h3 id="Configuration_dURL">Configuration d'URL</h3>
+### Configuration d'URL
-<p>Avant de créer notre vue, ajoutons une configuration d'URL pour la page <em>renew-books</em>. Copiez la configuration suivante à la fin de <strong>locallibrary/catalog/urls.py</strong>.</p>
+Avant de créer notre vue, ajoutons une configuration d'URL pour la page _renew-books_. Copiez la configuration suivante à la fin de **locallibrary/catalog/urls.py**.
-<pre class="brush: python">urlpatterns += [
- url(r'^book/(?P&lt;pk&gt;[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'),
-]</pre>
+```python
+urlpatterns += [
+ url(r'^book/(?P<pk>[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'),
+]
+```
-<p>La configuration d'URL va rediriger les URLs ayant le format <strong>/catalog/book/<em>&lt;bookinstance id&gt;</em>/renew/</strong> vers la fonction appelée <code>renew_book_librarian()</code> dans <strong>views.py</strong>, et envoyer l'id de <code>BookInstance</code> comme paramètre sous le nom <code>pk</code>. Le pattern ne fonctionnera que si <code>pk</code> est un <code>uuid</code> correctement formaté.</p>
+La configuration d'URL va rediriger les URLs ayant le format **/catalog/book/_\<bookinstance id>_/renew/** vers la fonction appelée `renew_book_librarian()` dans **views.py**, et envoyer l'id de `BookInstance` comme paramètre sous le nom `pk`. Le pattern ne fonctionnera que si `pk` est un `uuid` correctement formaté.
-<div class="note">
-<p><strong>Note :</strong> Nous pouvons appeler comme bon nous semble la donnée d'URL "<code>pk</code>" que nous avons capturée, car nous contrôlons complètement la fonction de notre <em>view</em> (nous n'utilisons pas une vue générique <em>detail</em>, laquelle attendrait des paramètres avec un certain nom). Cependant, le raccourci <code>pk</code>, pour "primary key", est une convention qu'il est raisonnable d'utiliser !</p>
-</div>
+> **Note :** Nous pouvons appeler comme bon nous semble la donnée d'URL "`pk`" que nous avons capturée, car nous contrôlons complètement la fonction de notre _view_ (nous n'utilisons pas une vue générique _detail_, laquelle attendrait des paramètres avec un certain nom). Cependant, le raccourci `pk`, pour "primary key", est une convention qu'il est raisonnable d'utiliser !
-<h3 id="Vue">Vue</h3>
+### Vue
-<p>Comme nous l'avons expliqué ci-dessus dans <a href="/fr/docs/Learn/Server-side/Django/Forms#django_form_handling_process">Django form handling process</a>, la vue doit retourner le formulaire par défaut s'il est appelé pour la première fois, et ensuite soit le retourner à nouveau avec les messages d'erreur si les données sont invalides, soit gérer les données et rediriger vers une nouvelle page si elles sont valides. Pour effectuer ces différentes actions, la vue doit être en mesure de savoir si elle est appelée pour la première fois (et retourner le formulaire par défaut) ou pour la deuxième fois ou plus (et valider les données).</p>
+Comme nous l'avons expliqué ci-dessus dans [Django form handling process](/fr/docs/Learn/Server-side/Django/Forms#django_form_handling_process), la vue doit retourner le formulaire par défaut s'il est appelé pour la première fois, et ensuite soit le retourner à nouveau avec les messages d'erreur si les données sont invalides, soit gérer les données et rediriger vers une nouvelle page si elles sont valides. Pour effectuer ces différentes actions, la vue doit être en mesure de savoir si elle est appelée pour la première fois (et retourner le formulaire par défaut) ou pour la deuxième fois ou plus (et valider les données).
-<p>Pour les formulaires qui utilisent une requête <code>POST</code> pour envoyer une information au serveur, la manière la plus commune de procéder pour la vue est de tester le type de requête <code>POST</code> (<code>if request.method == 'POST':</code>) pour repérer des requêtes de type validation de formulaire, et <code>GET</code> (en utilisant une condition <code>else</code>) pour identifer une requête initiale de création de formulaire. Si vous voulez utiliser une requête <code>GET</code> pour envoyer vos données, alors une approche classique pour savoir si la vue est invoquée pour la première fois ou non est de lire les données du formulaire (p. ex. lire une valeur cachée dans le formulaire).</p>
+Pour les formulaires qui utilisent une requête `POST` pour envoyer une information au serveur, la manière la plus commune de procéder pour la vue est de tester le type de requête `POST` (`if request.method == 'POST':`) pour repérer des requêtes de type validation de formulaire, et `GET` (en utilisant une condition `else`) pour identifer une requête initiale de création de formulaire. Si vous voulez utiliser une requête `GET` pour envoyer vos données, alors une approche classique pour savoir si la vue est invoquée pour la première fois ou non est de lire les données du formulaire (p. ex. lire une valeur cachée dans le formulaire).
-<p>Le processus de renouvellement de livre va écrire dans notre base de données, aussi, par convention, nous utiliserons le type de requête <code>POST</code>. Le bout de code ci-dessous montre le procédé (très classique) pour cette sorte de vue basée sur des fonctions.</p>
+Le processus de renouvellement de livre va écrire dans notre base de données, aussi, par convention, nous utiliserons le type de requête `POST`. Le bout de code ci-dessous montre le procédé (très classique) pour cette sorte de vue basée sur des fonctions.
-<pre class="brush: python">import datetime
+```python
+import datetime
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
@@ -232,13 +232,13 @@ def renew_book_librarian(request, pk):
    book_inst=get_object_or_404(BookInstance, pk = pk)
    # If this is a POST request then process the Form data
-<strong>    if request.method == 'POST':</strong>
+    if request.method == 'POST':
        # Create a form instance and populate it with data from the request (binding):
        form = RenewBookForm(request.POST)
        # Check if the form is valid:
-        <strong>if form.is_valid():</strong>
+        if form.is_valid():
            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            book_inst.due_back = form.cleaned_data['renewal_date']
            book_inst.save()
@@ -247,43 +247,45 @@ def renew_book_librarian(request, pk):
            return HttpResponseRedirect(reverse('all-borrowed') )
    # If this is a GET (or any other method) create the default form.
-<strong>    else:</strong>
+    else:
        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
-    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
+```
-<p>Nous importons tout d'abord notre formulaire (<code>RenewBookForm</code>) et un certain nombre d'autres objets/méthodes utiles, dont nous nous servons dans le corps de la fonction de notre vue :</p>
+Nous importons tout d'abord notre formulaire (`RenewBookForm`) et un certain nombre d'autres objets/méthodes utiles, dont nous nous servons dans le corps de la fonction de notre vue :
-<ul>
- <li><code><a href="https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#get-object-or-404">get_object_or_404()</a></code> : Retourne un certain objet depuis un modèle, en se basant sur sa valeur "primary key", et lève une exception <code>Http404</code> (<em>not found</em>) si l'enregistrement n'existe pas.</li>
- <li><code><a href="https://docs.djangoproject.com/en/1.10/ref/request-response/#django.http.HttpResponseRedirect">HttpResponseRedirect</a></code> : Cette méthode crée une redirection vers une certaine URL (code de statut HTTP 302).</li>
- <li><code><a href="https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#django.urls.reverse">reverse()</a></code> : Cette méthode génère une URL à partir d'un nom trouvé dans la configuration d'URL et un ensemble d'arguments. C'est l'équivalent Python du tag <code>url</code> que nous avons utilisé dans nos templates.</li>
- <li><code><a href="https://docs.python.org/3/library/datetime.html">datetime</a></code> : Une bibliothèque Python pour manipuler des dates et des heures.</li>
-</ul>
+- [`get_object_or_404()`](https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#get-object-or-404) : Retourne un certain objet depuis un modèle, en se basant sur sa valeur "primary key", et lève une exception `Http404` (_not found_) si l'enregistrement n'existe pas.
+- [`HttpResponseRedirect`](https://docs.djangoproject.com/en/1.10/ref/request-response/#django.http.HttpResponseRedirect) : Cette méthode crée une redirection vers une certaine URL (code de statut HTTP 302).
+- [`reverse()`](https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#django.urls.reverse) : Cette méthode génère une URL à partir d'un nom trouvé dans la configuration d'URL et un ensemble d'arguments. C'est l'équivalent Python du tag `url` que nous avons utilisé dans nos templates.
+- [`datetime`](https://docs.python.org/3/library/datetime.html) : Une bibliothèque Python pour manipuler des dates et des heures.
-<p>Dans la vue, nous utilisons d'abord l'argument <code>pk</code> dans la fonction <code>get_object_or_404()</code>, afin d'obtenir la <code>BookInstance</code> courante (si cette instance n'existe pas, la vue se termine immédiatement et la page va afficher une erreur ). Si ce n'est <em>pas</em> une requête <code>POST</code> (cas géré par la clause <code>else</code>), alors nous créons le formulaire par défaut en lui passant une valeur <code>initial</code> pour le champ <code>renewal_date</code> (comme montré en gras ci-dessous, c'est la date actuelle plus 3 semaines).</p>
+Dans la vue, nous utilisons d'abord l'argument `pk` dans la fonction `get_object_or_404()`, afin d'obtenir la `BookInstance` courante (si cette instance n'existe pas, la vue se termine immédiatement et la page va afficher une erreur ). Si ce n'est _pas_ une requête `POST` (cas géré par la clause `else`), alors nous créons le formulaire par défaut en lui passant une valeur `initial` pour le champ `renewal_date` (comme montré en gras ci-dessous, c'est la date actuelle plus 3 semaines).
-<pre class="brush: python"> book_inst=get_object_or_404(BookInstance, pk = pk)
+```python
+ book_inst=get_object_or_404(BookInstance, pk = pk)
    # If this is a GET (or any other method) create the default form
-    <strong>else:</strong>
-        proposed_renewal_date = datetime.date.today() + datetime.timedelta(<strong>weeks=3</strong>)
-        <strong>form = RenewBookForm(initial={'</strong>renewal_date<strong>': </strong>proposed_renewal_date<strong>,})</strong>
+    else:
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
-    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
+```
-<p>Après création du formulaire, nous appelons la fonction <code>render()</code> pour créer la page HTML, en précisant le template et un contexte qui contient notre formulaire. Dans ce cas, le contexte contient aussi notre <code>BookInstance</code>, que nous allons utiliser dans le template pour fournir des informations à propos du livre que nous sommes en train de renouveler.</p>
+Après création du formulaire, nous appelons la fonction `render()` pour créer la page HTML, en précisant le template et un contexte qui contient notre formulaire. Dans ce cas, le contexte contient aussi notre `BookInstance`, que nous allons utiliser dans le template pour fournir des informations à propos du livre que nous sommes en train de renouveler.
-<p>En revanche, s'il s'agit d'une requête <code>POST</code>, alors nous créons notre objet <code>form</code> et le peuplons avec des données récupérées dans la requête. Ce processus est appelé "binding" (liaison) et nous permet de valider le formulaire. Ensuite nous vérifions que le formulaire est valide, ce qui déclenche tout le code de validation sur tous les champs - ce qui inclut à la fois le code générique vérifiant que notre champ date est effectivement une date valide, et notre fonction <code>clean_renewal_date()</code>, spécifique à notre formulaire, pour vérifier que la date est dans le bon intervalle.</p>
+En revanche, s'il s'agit d'une requête `POST`, alors nous créons notre objet `form` et le peuplons avec des données récupérées dans la requête. Ce processus est appelé "binding" (liaison) et nous permet de valider le formulaire. Ensuite nous vérifions que le formulaire est valide, ce qui déclenche tout le code de validation sur tous les champs - ce qui inclut à la fois le code générique vérifiant que notre champ date est effectivement une date valide, et notre fonction `clean_renewal_date()`, spécifique à notre formulaire, pour vérifier que la date est dans le bon intervalle.
-<pre class="brush: python">    book_inst=get_object_or_404(BookInstance, pk = pk)
+```python
+    book_inst=get_object_or_404(BookInstance, pk = pk)
    # If this is a POST request then process the Form data
    if request.method == 'POST':
        # Create a form instance and populate it with data from the request (binding):
-<strong>        form = RenewBookForm(request.POST)</strong>
+        form = RenewBookForm(request.POST)
        # Check if the form is valid:
        if form.is_valid():
@@ -294,236 +296,233 @@ def renew_book_librarian(request, pk):
            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed') )
-    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
+```
-<p>Si le formulaire n'est pas valide, nous appelons aussi la fonction <code>render()</code>, mais cette fois les valeurs passées dans le contexte vont inclure les messages d'erreur.</p>
+Si le formulaire n'est pas valide, nous appelons aussi la fonction `render()`, mais cette fois les valeurs passées dans le contexte vont inclure les messages d'erreur.
-<p>Si le formulaire est valide, alors nous pouvons commencer à utiliser les données, en y accédant à travers l'attribut <code>form.cleaned_data</code> (p. ex. <code>data = form.cleaned_data['renewal_date']</code>). Ici nous ne faisons que sauvegarder dans la valeur <code>due_back</code> de l'objet <code>BookInstance</code> associé les données reçues.</p>
+Si le formulaire est valide, alors nous pouvons commencer à utiliser les données, en y accédant à travers l'attribut `form.cleaned_data` (p. ex. `data = form.cleaned_data['renewal_date']`). Ici nous ne faisons que sauvegarder dans la valeur `due_back` de l'objet `BookInstance` associé les données reçues.
-<div class="warning">
-<p><strong>Attention :</strong> Alors que vous pouvez accéder aussi aux données de formulaire directement à travers la requête (par exemple <code>request.POST['renewal_date']</code>, ou <code>request.GET['renewal_date']</code> si vous utilisez une requête GET), ce n'est PAS recommandé. Les données nettoyées sont assainies, validées et converties en types standard Python.</p>
-</div>
+> **Attention :** Alors que vous pouvez accéder aussi aux données de formulaire directement à travers la requête (par exemple `request.POST['renewal_date']`, ou `request.GET['renewal_date']` si vous utilisez une requête GET), ce n'est PAS recommandé. Les données nettoyées sont assainies, validées et converties en types standard Python.
-<p>L'étape finale dans la partie "gestion de formulaire" de la vue est de rediriger vers une autre page, habituellement une page "success". Dans ce cas, nous utilisons <code>HttpResponseRedirect</code> et <code>reverse()</code> pour rediriger vers la vue appelée <code>'all-borrowed'</code> (qui a été créée dans la partie "challenge" de <a href="/fr/docs/Learn/Server-side/Django/authentication_and_sessions#Challenge_yourself">Django Tutorial Part 8: User authentication and permissions</a>. Si vous n'avez pas créé cette page, vous pouvez rediriger vers la page d'accueil à l'URL '/').</p>
+L'étape finale dans la partie "gestion de formulaire" de la vue est de rediriger vers une autre page, habituellement une page "success". Dans ce cas, nous utilisons `HttpResponseRedirect` et `reverse()` pour rediriger vers la vue appelée `'all-borrowed'` (qui a été créée dans la partie "challenge" de [Django Tutorial Part 8: User authentication and permissions](/fr/docs/Learn/Server-side/Django/authentication_and_sessions#Challenge_yourself). Si vous n'avez pas créé cette page, vous pouvez rediriger vers la page d'accueil à l'URL '/').
-<p>C'est tout ce qui est requis pour la gestion du formulaire lui-même, mais il nous faut encore restreindre l'accès à la vue aux seuls libraires. Nous devrions sans doute créer un nouveau réglage de permission dans <code>BookInstance</code> ("<code>can_renew</code>"), mais, pour ne pour ne pas compliquer les choses ici, nous allons simplement utiliser le décorateur de fonction <code>@permission_required</code> avec notre permission existante <code>can_mark_returned</code>.</p>
+C'est tout ce qui est requis pour la gestion du formulaire lui-même, mais il nous faut encore restreindre l'accès à la vue aux seuls libraires. Nous devrions sans doute créer un nouveau réglage de permission dans `BookInstance` ("`can_renew`"), mais, pour ne pour ne pas compliquer les choses ici, nous allons simplement utiliser le décorateur de fonction `@permission_required` avec notre permission existante `can_mark_returned`.
-<p>Le résultat final de la vue est donc comme indiqué ci-dessous. Veuillez copier ceci en bas de <strong>locallibrary/catalog/views.py</strong>.</p>
+Le résultat final de la vue est donc comme indiqué ci-dessous. Veuillez copier ceci en bas de **locallibrary/catalog/views.py**.
-<pre><strong>from django.contrib.auth.decorators import permission_required</strong>
+ from django.contrib.auth.decorators import permission_required
-from django.shortcuts import get_object_or_404
-from django.http import HttpResponseRedirect
-from django.core.urlresolvers import reverse
-import datetime
+ from django.shortcuts import get_object_or_404
+ from django.http import HttpResponseRedirect
+ from django.core.urlresolvers import reverse
+ import datetime
-from .forms import RenewBookForm
+ from .forms import RenewBookForm
-<strong>@permission_required('catalog.<code>can_mark_returned</code>')</strong>
-def renew_book_librarian(request, pk):
-    """
-    View function for renewing a specific BookInstance by librarian
-    """
-    book_inst=get_object_or_404(BookInstance, pk = pk)
+ @permission_required('catalog.can_mark_returned')
+ def renew_book_librarian(request, pk):
+     """
+     View function for renewing a specific BookInstance by librarian
+     """
+     book_inst=get_object_or_404(BookInstance, pk = pk)
-    # If this is a POST request then process the Form data
-    if request.method == 'POST':
+     # If this is a POST request then process the Form data
+     if request.method == 'POST':
-        # Create a form instance and populate it with data from the request (binding):
-        form = RenewBookForm(request.POST)
+         # Create a form instance and populate it with data from the request (binding):
+         form = RenewBookForm(request.POST)
-        # Check if the form is valid:
-        if form.is_valid():
-            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
-            book_inst.due_back = form.cleaned_data['renewal_date']
-            book_inst.save()
+         # Check if the form is valid:
+         if form.is_valid():
+             # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+             book_inst.due_back = form.cleaned_data['renewal_date']
+             book_inst.save()
-            # redirect to a new URL:
-            return HttpResponseRedirect(reverse('all-borrowed') )
+             # redirect to a new URL:
+             return HttpResponseRedirect(reverse('all-borrowed') )
-    # If this is a GET (or any other method) create the default form.
-    else:
-        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
-        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
+     # If this is a GET (or any other method) create the default form.
+     else:
+         proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+         form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
-    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
-</pre>
+     return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
-<h3 id="Le_template">Le template</h3>
+### Le template
-<p>Créez le template référencé dans la vue (<strong>/catalog/templates/catalog/book_renew_librarian.html</strong>) et copiez-y le code suivant :</p>
+Créez le template référencé dans la vue (**/catalog/templates/catalog/book_renew_librarian.html**) et copiez-y le code suivant :
-<pre class="brush: html">{% extends "base_generic.html" %}
+```html
+{% extends "base_generic.html" %}
{% block content %}
- &lt;h1&gt;Renew: \{{bookinst.book.title}}&lt;/h1&gt;
- &lt;p&gt;Borrower: \{{bookinst.borrower}}&lt;/p&gt;
- &lt;p{% if bookinst.is_overdue %} class="text-danger"{% endif %}&gt;Due date: \{{bookinst.due_back}}&lt;/p&gt;
+ <h1>Renew: \{{bookinst.book.title}}</h1>
+ <p>Borrower: \{{bookinst.borrower}}</p>
+ <p{% if bookinst.is_overdue %} class="text-danger"{% endif %}>Due date: \{{bookinst.due_back}}</p>
-<strong> &lt;form action="" method="post"&gt;
+ <form action="" method="post">
{% csrf_token %}
- &lt;table&gt;
+ <table>
\{{ form }}
-  &lt;/table&gt;
- &lt;input type="submit" value="Submit" /&gt;
- &lt;/form&gt;</strong>
+  </table>
+ <input type="submit" value="Submit" />
+ </form>
-{% endblock %}</pre>
+{% endblock %}
+```
-<p>La majeure partie de ce code devrait vous être familière si vous avez suivi les tutoriels précédents. Nous étendons le template de base et ensuite redéfinissons le block "content". Nous sommes en mesure de référencer <code>\{{ book_instance }}</code> (et ses variables), puisqu'il a été passé dans l'objet context par la fonction <code>render()</code>, et nous utilisons tout cela pour lister le titre du livre, son emprunteur et la date originale de retour.</p>
+La majeure partie de ce code devrait vous être familière si vous avez suivi les tutoriels précédents. Nous étendons le template de base et ensuite redéfinissons le block "content". Nous sommes en mesure de référencer `\{{ book_instance }}` (et ses variables), puisqu'il a été passé dans l'objet context par la fonction `render()`, et nous utilisons tout cela pour lister le titre du livre, son emprunteur et la date originale de retour.
-<p>Le code du formulaire est relativement simple. Nous déclarons d'abord les tags <code>form</code>, en précisant où le formulaire doit être adressé (<code>action</code>) et la <code>method</code> utilisée pour soumettre les donées (ici un "HTTP POST"). Si vous vous rappelez ce qui a été dit en haut de cette page (aperçu sur les <a href="/fr/docs/Learn/Server-side/Django/Forms#HTML_forms">HTML Forms</a>), une <code>action</code> vide comme ici signifie que les données de formulaire seront postées à nouveau à l'URL actuelle (ce qui est le comportement que nous voulons !). À l'intérieur des tags, nous définissons le bouton , sur lequel l'utilisateur peut appuyer pour envoyer les données. Le <code>{% csrf_token %}</code> ajouté juste à l'intérieur des tags "form" est un des éléments de protection utilisés par Django contre les "cross-site forgery".</p>
+Le code du formulaire est relativement simple. Nous déclarons d'abord les tags `form`, en précisant où le formulaire doit être adressé (`action`) et la `method` utilisée pour soumettre les donées (ici un "HTTP POST"). Si vous vous rappelez ce qui a été dit en haut de cette page (aperçu sur les [HTML Forms](/fr/docs/Learn/Server-side/Django/Forms#HTML_forms)), une `action` vide comme ici signifie que les données de formulaire seront postées à nouveau à l'URL actuelle (ce qui est le comportement que nous voulons !). À l'intérieur des tags, nous définissons le bouton , sur lequel l'utilisateur peut appuyer pour envoyer les données. Le `{% csrf_token %}` ajouté juste à l'intérieur des tags "form" est un des éléments de protection utilisés par Django contre les "cross-site forgery".
-<div class="note">
-<p><strong>Note :</strong> Ajoutez le <code>{% csrf_token %}</code> à tout template Django que vous créeez et qui utilise <code>POST</code> pour soumettre les données. Cela réduira les risques qu'un utilisateur mal intentionné pirate vos formulaires.</p>
-</div>
+> **Note :** Ajoutez le `{% csrf_token %}` à tout template Django que vous créeez et qui utilise `POST` pour soumettre les données. Cela réduira les risques qu'un utilisateur mal intentionné pirate vos formulaires.
-<p>Tout ce qui reste est la variable de template <code>\{{ form }}</code>, que nous avons passée au template dans le dictionnaire de contexte. Peut-être sans surprise, quand il est utilisé comme indiqué, il fournit le rendu par défaut de tous les champs de formulaire, y compris leurs labels, widgets et textes d'aide. Voici le rendu :</p>
+Tout ce qui reste est la variable de template `\{{ form }}`, que nous avons passée au template dans le dictionnaire de contexte. Peut-être sans surprise, quand il est utilisé comme indiqué, il fournit le rendu par défaut de tous les champs de formulaire, y compris leurs labels, widgets et textes d'aide. Voici le rendu :
-<pre class="brush: html">&lt;tr&gt;
-  &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;
-  &lt;td&gt;
-  &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required /&gt;
-  &lt;br /&gt;
-  &lt;span class="helptext"&gt;Enter date between now and 4 weeks (default 3 weeks).&lt;/span&gt;
-  &lt;/td&gt;
-&lt;/tr&gt;
-</pre>
+```html
+<tr>
+  <th><label for="id_renewal_date">Renewal date:</label></th>
+  <td>
+  <input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required />
+  <br />
+  <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
+  </td>
+</tr>
+```
-<div class="note">
-<p><strong>Note :</strong> Ce n'est peut-être pas évident, car nous n'avons qu'un seul champ, mais, par défaut, chaque champ est défini dans sa propre ligne de tableau. Ce même rendu est fourni si vous référencez la variable de template <code>\{{ form.as_table }}</code>.</p>
-</div>
+> **Note :** Ce n'est peut-être pas évident, car nous n'avons qu'un seul champ, mais, par défaut, chaque champ est défini dans sa propre ligne de tableau. Ce même rendu est fourni si vous référencez la variable de template `\{{ form.as_table }}`.
-<p>Si vous aviez entré une date invalide, vous obtiendriez en plus sur la page une liste des erreurs (indiquées en gras ci-dessous).</p>
+Si vous aviez entré une date invalide, vous obtiendriez en plus sur la page une liste des erreurs (indiquées en gras ci-dessous).
-<pre class="brush: html">&lt;tr&gt;
-  &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;
-  &lt;td&gt;
-<strong>  &lt;ul class="errorlist"&gt;
-  &lt;li&gt;Invalid date - renewal in past&lt;/li&gt;
-  &lt;/ul&gt;</strong>
-  &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required /&gt;
-  &lt;br /&gt;
-  &lt;span class="helptext"&gt;Enter date between now and 4 weeks (default 3 weeks).&lt;/span&gt;
- &lt;/td&gt;
-&lt;/tr&gt;</pre>
+```html
+<tr>
+  <th><label for="id_renewal_date">Renewal date:</label></th>
+  <td>
+  <ul class="errorlist">
+  <li>Invalid date - renewal in past</li>
+  </ul>
+  <input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required />
+  <br />
+  <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
+ </td>
+</tr>
+```
-<h4 id="Autres_façons_dutiliser_la_variable_de_template_du_formulaire">Autres façons d'utiliser la variable de template du formulaire</h4>
+#### Autres façons d'utiliser la variable de template du formulaire
-<p>Si vous utilisez <code>\{{ form.as_table }}</code> comme indiqué ci-dessus, chaque champ est rendu comme une ligne de tableau. Vous pouvez également rendre chaque champ comme un élément de liste (en utilisant <code>\{{ form.as_ul }}</code>) ou comme un paragraphe (en utilisant <code>\{{ form.as_p }}</code>).</p>
+Si vous utilisez `\{{ form.as_table }}` comme indiqué ci-dessus, chaque champ est rendu comme une ligne de tableau. Vous pouvez également rendre chaque champ comme un élément de liste (en utilisant `\{{ form.as_ul }}`) ou comme un paragraphe (en utilisant `\{{ form.as_p }}`).
-<p>Il est également possible d'avoir un contrôle complet sur le rendu de chaque partie du formulaire, en indexant ses propriétés grâce à la notation pointée. Ainsi, par exemple, nous pouvons accéder un certain nombre d'éléments distincts pour notre champ <code>renewal_date</code> :</p>
+Il est également possible d'avoir un contrôle complet sur le rendu de chaque partie du formulaire, en indexant ses propriétés grâce à la notation pointée. Ainsi, par exemple, nous pouvons accéder un certain nombre d'éléments distincts pour notre champ `renewal_date` :
-<ul>
- <li><code>\{{form.renewal_date}}</code>  : Le champ complet.</li>
- <li><code>\{{form.renewal_date.errors}}</code> : La liste des erreurs.</li>
- <li><code>\{{form.renewal_date.id_for_label}}</code> : L'id du label.</li>
- <li><code>\{{form.renewal_date.help_text}}</code> : Le texte d'aide du champ.</li>
- <li>etc !</li>
-</ul>
+- `\{{form.renewal_date}}`  : Le champ complet.
+- `\{{form.renewal_date.errors}}` : La liste des erreurs.
+- `\{{form.renewal_date.id_for_label}}` : L'id du label.
+- `\{{form.renewal_date.help_text}}` : Le texte d'aide du champ.
+- etc !
-<p>Pour plus d'exemples sur la manière de rendre manuellement des formulaires dans des templates, et boucler de manière dynamique sur les champs du template, voyez <a href="https://docs.djangoproject.com/en/1.10/topics/forms/#rendering-fields-manually">Working with forms &gt; Rendering fields manually</a> (Django docs).</p>
+Pour plus d'exemples sur la manière de rendre manuellement des formulaires dans des templates, et boucler de manière dynamique sur les champs du template, voyez [Working with forms > Rendering fields manually](https://docs.djangoproject.com/en/1.10/topics/forms/#rendering-fields-manually) (Django docs).
-<h3 id="Tester_la_page">Tester la page</h3>
+### Tester la page
-<p>Si vous avez accepté le "challenge" dans <a href="/fr/docs/Learn/Server-side/Django/authentication_and_sessions#Challenge_yourself">Django Tutorial Part 8: User authentication and permissions</a>, vous avez une liste de tous les livres empruntés dans la bibliothèque, ce qui n'est visible que pour le staff de la bibliothèque. Nous pouvons ajouter un lien vers notre page de renouvellement après chaque élément, en utilisant le code de template suivant.</p>
+Si vous avez accepté le "challenge" dans [Django Tutorial Part 8: User authentication and permissions](/fr/docs/Learn/Server-side/Django/authentication_and_sessions#Challenge_yourself), vous avez une liste de tous les livres empruntés dans la bibliothèque, ce qui n'est visible que pour le staff de la bibliothèque. Nous pouvons ajouter un lien vers notre page de renouvellement après chaque élément, en utilisant le code de template suivant.
-<pre class="brush: html">{% if perms.catalog.can_mark_returned %}- &lt;a href="{% url 'renew-book-librarian' bookinst.id %}"&gt;Renew&lt;/a&gt; {% endif %}</pre>
+```html
+{% if perms.catalog.can_mark_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a> {% endif %}
+```
-<div class="note">
-<p><strong>Note :</strong> Souvenez-vous que votre login de test devra avoir la permission "<code>catalog.can_mark_returned</code>" pour pouvoir accéder la page de renouvellement de livre (utilisez peut-être votre compte superuser).</p>
-</div>
+> **Note :** Souvenez-vous que votre login de test devra avoir la permission "`catalog.can_mark_returned`" pour pouvoir accéder la page de renouvellement de livre (utilisez peut-être votre compte superuser).
-<p>Vous pouvez aussi construire manuellement une URL de test comme ceci : <a class="external" href="http://127.0.0.1:8000/catalog/book/&lt;bookinstance id>/renew/" rel="noopener">http://127.0.0.1:8000/catalog/book/<em>&lt;bookinstance_id&gt;</em>/renew/</a> (un id de bookinstance valide peut être obtenu en navigant vers une page de détail de livre dans votre bibliothèque, et en copiant le champ <code>id</code>).</p>
+Vous pouvez aussi construire manuellement une URL de test comme ceci : [http://127.0.0.1:8000/catalog/book/*\<bookinstance_id>*/renew/](<http://127.0.0.1:8000/catalog/book/\<bookinstance id\>/renew/>) (un id de bookinstance valide peut être obtenu en navigant vers une page de détail de livre dans votre bibliothèque, et en copiant le champ `id`).
-<h3 id="À_quoi_cela_ressemble-t-il">À quoi cela ressemble-t-il ?</h3>
+### À quoi cela ressemble-t-il ?
-<p>Si tout a bien marché, le formulaire par défaut ressemblera à ceci :</p>
+Si tout a bien marché, le formulaire par défaut ressemblera à ceci :
-<p><img alt="" src="forms_example_renew_default.png"></p>
+![](forms_example_renew_default.png)
-<p>Le formulaire avec valeur erronée ressemblera à ceci :</p>
+Le formulaire avec valeur erronée ressemblera à ceci :
-<p><img alt="" src="forms_example_renew_invalid.png"></p>
+![](forms_example_renew_invalid.png)
-<p>La liste de tous les livres avec les liens vers le renouvellement ressemblera à ceci :</p>
+La liste de tous les livres avec les liens vers le renouvellement ressemblera à ceci :
-<p><img alt="" src="forms_example_renew_allbooks.png"></p>
+![](forms_example_renew_allbooks.png)
-<h2 id="ModelForms">ModelForms</h2>
+## ModelForms
-<p>Créer une classe  en utilisant l'approche décrite ci-dessus est très flexible et vous autorise à créer le type de page de formulaire que vous voulez, et à l'associer à tout type de modèle(s).</p>
+Créer une classe  en utilisant l'approche décrite ci-dessus est très flexible et vous autorise à créer le type de page de formulaire que vous voulez, et à l'associer à tout type de modèle(s).
-<p>Cependant, si vous avez seulement besoin d'un formulaire qui répertorie les champs d'un modèle unique, alors votre modèle définira déjà la plupart des informations requises dans votre formulaire : champs, labels, texte d'aide etc. Plutôt que de créer à nouveau les définitions du modèle dans votre formulaire, il est plus facile d'utiliser la classe d'aide <a class="external" href="https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/" rel="noopener">ModelForm</a> pour créer le formulaire d'après votre modèle. Ce <code>ModelForm</code> peut dès lors être utilisé à l'intérieur de vos vues exactement de la même manière qu'un <code>Form</code> ordinaire.</p>
+Cependant, si vous avez seulement besoin d'un formulaire qui répertorie les champs d'un modèle unique, alors votre modèle définira déjà la plupart des informations requises dans votre formulaire : champs, labels, texte d'aide etc. Plutôt que de créer à nouveau les définitions du modèle dans votre formulaire, il est plus facile d'utiliser la classe d'aide [ModelForm](https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/) pour créer le formulaire d'après votre modèle. Ce `ModelForm` peut dès lors être utilisé à l'intérieur de vos vues exactement de la même manière qu'un `Form` ordinaire.
-<p>Un <code>ModelForm</code> basique, contenant le même champ que notre <code>RenewBookForm</code> d'origine, est montré ci-dessous. Tout ce que vous avez à faire pour créer le formulaire, c'est ajouter <code>class Meta</code> avec le <code>model</code> (<code>BookInstance</code>) associé, et une liste des <code>fields</code> du modèle à inclure dans le formulaire (vous pouvez inclure tous les champs en utilisant <code>fields = '__all__'</code>, ou bien utiliser <code>exclude</code> (au lieu de <code>fields</code>) pour préciser les champs à ne <em>pas</em> importer du modèle).</p>
+Un `ModelForm` basique, contenant le même champ que notre `RenewBookForm` d'origine, est montré ci-dessous. Tout ce que vous avez à faire pour créer le formulaire, c'est ajouter `class Meta` avec le `model` (`BookInstance`) associé, et une liste des `fields` du modèle à inclure dans le formulaire (vous pouvez inclure tous les champs en utilisant `fields = '__all__'`, ou bien utiliser `exclude` (au lieu de `fields`) pour préciser les champs à ne _pas_ importer du modèle).
-<pre class="brush: python">from django.forms import ModelForm
+```python
+from django.forms import ModelForm
from .models import BookInstance
class RenewBookModelForm(ModelForm):
-<strong> class Meta:
+ class Meta:
model = BookInstance
- fields = ['due_back',]</strong>
-</pre>
+ fields = ['due_back',]
+```
-<div class="note">
-<p><strong>Note :</strong> Cela peut ne pas sembler beaucoup plus simple que d'utiliser un simple <code>Form</code>, et ça ne l'est effectivement pas dans ce cas, parce que nous n'avons qu'un seul champ. Cependant, si vous avez beaucoup de champs, cela peut réduire notablement la quantité de code !</p>
-</div>
+> **Note :** Cela peut ne pas sembler beaucoup plus simple que d'utiliser un simple `Form`, et ça ne l'est effectivement pas dans ce cas, parce que nous n'avons qu'un seul champ. Cependant, si vous avez beaucoup de champs, cela peut réduire notablement la quantité de code !
-<p>Le reste de l'information vient des définitions de champ données par le modèle (par ex. les labels, les widgets, le texte d'aide, les messages d'erreur). S'ils ne sont pas suffisamment satisfaisants, nous pouvons les réécrire dans notre <code>class Meta</code>, en précisant un dictionnaire contenant le champ à modifier et sa nouvelle valeur. Par exemple, dans ce formulaire, nous pourrions souhaiter, pour notre champ, un label tel que "<em>Renewal date</em>" (plutôt que celui par défaut, basé sur le nom du champ : <em>Due Back</em>), et nous voulons aussi que notre texte d'aide soit spécifique à ce cas d'utilisation. La classe <code>Meta</code> ci-dessous vous montre comment réécrire ces champs, et vous pouvez pareillement définir <code>widgets</code> et <code>error_messages</code> si les valeurs par défaut ne sont pas suffisantes.</p>
+Le reste de l'information vient des définitions de champ données par le modèle (par ex. les labels, les widgets, le texte d'aide, les messages d'erreur). S'ils ne sont pas suffisamment satisfaisants, nous pouvons les réécrire dans notre `class Meta`, en précisant un dictionnaire contenant le champ à modifier et sa nouvelle valeur. Par exemple, dans ce formulaire, nous pourrions souhaiter, pour notre champ, un label tel que "_Renewal date_" (plutôt que celui par défaut, basé sur le nom du champ : _Due Back_), et nous voulons aussi que notre texte d'aide soit spécifique à ce cas d'utilisation. La classe `Meta` ci-dessous vous montre comment réécrire ces champs, et vous pouvez pareillement définir `widgets` et `error_messages` si les valeurs par défaut ne sont pas suffisantes.
-<pre class="brush: python">class Meta:
+```python
+class Meta:
model = BookInstance
fields = ['due_back',]
-<strong> labels = { 'due_back': _('Renewal date'), }
- help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), } </strong>
-</pre>
+ labels = { 'due_back': _('Renewal date'), }
+ help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), }
+```
-<p>Pour ajouter une validation, vous pouvez utiliser la même approche que pour un <code>Form</code> normal : vous définissez une fonction appelée <code>clean_<em>field_name</em>()</code>, et vous levez des exceptions de type <code>ValidationError</code> pour les valeurs non valides. La seule différence par rapport à notre formulaire original, c'est que le champ de modèle est appelé <code>due_back</code> et non "<code>renewal_date</code>". Ce changement est nécessaire, dans la mesure où le champ correspondant dans <code>BookInstance</code> est appelé <code>due_back</code>.</p>
+Pour ajouter une validation, vous pouvez utiliser la même approche que pour un `Form` normal : vous définissez une fonction appelée `clean_field_name()`, et vous levez des exceptions de type `ValidationError` pour les valeurs non valides. La seule différence par rapport à notre formulaire original, c'est que le champ de modèle est appelé `due_back` et non "`renewal_date`". Ce changement est nécessaire, dans la mesure où le champ correspondant dans `BookInstance` est appelé `due_back`.
-<pre class="brush: python">from django.forms import ModelForm
+```python
+from django.forms import ModelForm
from .models import BookInstance
class RenewBookModelForm(ModelForm):
-<strong>    def clean_due_back(self):
+    def clean_due_back(self):
      data = self.cleaned_data['due_back']
  #Check date is not in past.
-       if data &lt; datetime.date.today():
+       if data < datetime.date.today():
          raise ValidationError(_('Invalid date - renewal in past'))
      #Check date is in range librarian allowed to change (+4 weeks)
-       if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+       if data > datetime.date.today() + datetime.timedelta(weeks=4):
          raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
      # Remember to always return the cleaned data.
      return data
-</strong>
+
class Meta:
model = BookInstance
fields = ['due_back',]
labels = { 'due_back': _('Renewal date'), }
help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), }
-</pre>
+```
-<p>La classe <code>RenewBookModelForm</code> ci-dessus est maintenant fonctionnellement équivalente à notre <code>RenewBookForm</code> d'origine. Vous pourriez l'importer et l'utiliser partout où vous utilisez <code>RenewBookForm</code>, du moment que vous changez aussi de <code>renewal_date</code> en <code>due_back</code> le nom de variable du formulaire correspondant, comme dans la deuxième déclaration du formulaire : <code>RenewBookModelForm(initial={'due_back': proposed_renewal_date}</code>.</p>
+La classe `RenewBookModelForm` ci-dessus est maintenant fonctionnellement équivalente à notre `RenewBookForm` d'origine. Vous pourriez l'importer et l'utiliser partout où vous utilisez `RenewBookForm`, du moment que vous changez aussi de `renewal_date` en `due_back` le nom de variable du formulaire correspondant, comme dans la deuxième déclaration du formulaire : `RenewBookModelForm(initial={'due_back': proposed_renewal_date}`.
-<h2 id="Vues_génériques_dédition">Vues génériques d'édition</h2>
+## Vues génériques d'édition
-<p>L'algorithme de gestion des formulaires que nous avons utilisé ci-dessus dans notre exemple de vue basée sur une fonction, représente un processus extrêmement commun dans vues destinées à éditer un formulaire. Django abstrait pour vous la plus grande partie de ce processus répétitif ("boilerplate") en proposant des <a class="external" href="https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-editing/" rel="noopener">generic editing views</a> pour les vues de création, éditition et suppression basées sur des modèles. Ces vues génériques non seulement assument le comportement d'une vue, mais elles créent automatiquement la classe de formulaire (un <code>ModelForm</code>) pour vous à partir du modèle.</p>
+L'algorithme de gestion des formulaires que nous avons utilisé ci-dessus dans notre exemple de vue basée sur une fonction, représente un processus extrêmement commun dans vues destinées à éditer un formulaire. Django abstrait pour vous la plus grande partie de ce processus répétitif ("boilerplate") en proposant des [generic editing views](https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-editing/) pour les vues de création, éditition et suppression basées sur des modèles. Ces vues génériques non seulement assument le comportement d'une vue, mais elles créent automatiquement la classe de formulaire (un `ModelForm`) pour vous à partir du modèle.
-<div class="note">
-<p><strong>Note :</strong> En plus des vues d'édition décrites ici, il existe aussi une classe <a class="external" href="https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-editing/#formview" rel="noopener">FormView</a>, qui se tient, en termes de rapport "flexibilité"/"effort codage", à mi-chemin entre notre vue basée sur une fonction et les autres vues génériques. En utilisant <code>FormView</code>, vous avez encore besoin de créer votre <code>Form</code>, mais vous n'avez pas besoin d'implémenter tous les éléments d'une gestion standard de formulaire. À la place, vous n'avez qu'à fournir une implémentation de la fonction qui sera appelée une fois que les données envoyées sont reconnues valides.</p>
-</div>
+> **Note :** En plus des vues d'édition décrites ici, il existe aussi une classe [FormView](https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-editing/#formview), qui se tient, en termes de rapport "flexibilité"/"effort codage", à mi-chemin entre notre vue basée sur une fonction et les autres vues génériques. En utilisant `FormView`, vous avez encore besoin de créer votre `Form`, mais vous n'avez pas besoin d'implémenter tous les éléments d'une gestion standard de formulaire. À la place, vous n'avez qu'à fournir une implémentation de la fonction qui sera appelée une fois que les données envoyées sont reconnues valides.
-<p>Dans cette section, nous allons utiliser des vues génériques d'édition pour créer des pages afin de pouvoir ajouter les fonctionnalités de création, d'édition et de suppression des enregistrements de type <code>Author</code> de notre bibliothèque, en fournissant efficacement une réimplémentation basique de certaines parties du site Admin (cela peut être intéressant si vous avez besoin d'offrir une fonctionnalité admin d'une manière plus flexible que ce qui peut être présenté par le site admin).</p>
+Dans cette section, nous allons utiliser des vues génériques d'édition pour créer des pages afin de pouvoir ajouter les fonctionnalités de création, d'édition et de suppression des enregistrements de type `Author` de notre bibliothèque, en fournissant efficacement une réimplémentation basique de certaines parties du site Admin (cela peut être intéressant si vous avez besoin d'offrir une fonctionnalité admin d'une manière plus flexible que ce qui peut être présenté par le site admin).
-<h3 id="Vues">Vues</h3>
+### Vues
-<p>Ouvrez le fichier vue (<strong>locallibrary/catalog/views.py</strong>) et ajoutez le bloc de code suivant à la fin :</p>
+Ouvrez le fichier vue (**locallibrary/catalog/views.py**) et ajoutez le bloc de code suivant à la fin :
-<pre class="brush: python">from django.views.generic.edit import CreateView, UpdateView, DeleteView
+```python
+from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Author
@@ -538,114 +537,113 @@ class AuthorUpdate(UpdateView):
class AuthorDelete(DeleteView):
model = Author
- success_url = reverse_lazy('authors')</pre>
+ success_url = reverse_lazy('authors')
+```
-<p>Comme vous pouvez le voir, pour les vues "créer", "modifier" et "supprimer", vous avez besoin de dériver respectivement des vues génériques <code>CreateView</code>, <code>UpdateView</code>, et <code>DeleteView</code>, et de définir ensuite le modèle associé.</p>
+Comme vous pouvez le voir, pour les vues "créer", "modifier" et "supprimer", vous avez besoin de dériver respectivement des vues génériques `CreateView`, `UpdateView`, et `DeleteView`, et de définir ensuite le modèle associé.
-<p>Pour les cas "créer" et "modifier", vous devez aussi préciser les champs à afficher dans le formulaire (en utilisant la même syntaxe que pour la classe <code>ModelForm</code>). Dans ce cas, nous montrons à la fois la syntaxe pour afficher "tous" les champs, et comment vous pouvez les lister un par un. Vous pouvez aussi spécifier les valeurs initiales pour chacun des champs, en utilisant un dictionnaire de paires <em>nom_du_champ/valeur</em> (ici nous définissons arbitrairement la date de mort, uniquement dans un but de démonstration - sans doute voudrez-vous l'enlever !). Par défaut, ces vues vont rediriger en cas de succès vers une page affichant l'élément nouvellement créé ou modifié, ce qui, dans notre cas, sera la vue "détail" d'un auteur, créée dans un précédent tutoriel. Vous pouvez spécifier un autre lieu de redirection en déclarant explicitement le paramètre <code>success_url</code> (comme indiqué dans la classe <code>AuthorDelete</code>).</p>
+Pour les cas "créer" et "modifier", vous devez aussi préciser les champs à afficher dans le formulaire (en utilisant la même syntaxe que pour la classe `ModelForm`). Dans ce cas, nous montrons à la fois la syntaxe pour afficher "tous" les champs, et comment vous pouvez les lister un par un. Vous pouvez aussi spécifier les valeurs initiales pour chacun des champs, en utilisant un dictionnaire de paires _nom_du_champ/valeur_ (ici nous définissons arbitrairement la date de mort, uniquement dans un but de démonstration - sans doute voudrez-vous l'enlever !). Par défaut, ces vues vont rediriger en cas de succès vers une page affichant l'élément nouvellement créé ou modifié, ce qui, dans notre cas, sera la vue "détail" d'un auteur, créée dans un précédent tutoriel. Vous pouvez spécifier un autre lieu de redirection en déclarant explicitement le paramètre `success_url` (comme indiqué dans la classe `AuthorDelete`).
-<p>La classe  ne requiert pas l'affichage d'aucun champ, aussi n'ont-ils pas besoin d'être précisés. Par contre il vous faut bien spécifier la <code>success_url</code>, car Django n'a pas de valeur par défaut pour cela. Dans ce cas, nous utilisons la fonction  pour rediriger vers notre liste d'auteurs après qu'un auteur ait été supprimé. <code>reverse_lazy()</code> est une version de <code>reverse()</code> exécutée mollement ("lazily"), que nous utilisons ici parce que nous fournissons une URL à un attribut de vue basée sur classe.</p>
+La classe  ne requiert pas l'affichage d'aucun champ, aussi n'ont-ils pas besoin d'être précisés. Par contre il vous faut bien spécifier la `success_url`, car Django n'a pas de valeur par défaut pour cela. Dans ce cas, nous utilisons la fonction  pour rediriger vers notre liste d'auteurs après qu'un auteur ait été supprimé. `reverse_lazy()` est une version de `reverse()` exécutée mollement ("lazily"), que nous utilisons ici parce que nous fournissons une URL à un attribut de vue basée sur classe.
-<h3 id="Templates">Templates</h3>
+### Templates
-<p>Les vues "créer" et "modifier" utilisent le même template par défaut, lequel sera nommé d'après votre modèle : <em>model_name</em><strong>_form.html</strong> (vous pouvez changer le suffixe en autre chose que <strong>_form</strong> en utilisant le champ <code>template_name_suffix</code> dans votre vue, par exemple <code>template_name_suffix = '_other_suffix'</code>).</p>
+Les vues "créer" et "modifier" utilisent le même template par défaut, lequel sera nommé d'après votre modèle : \*model_name**\*\_form.html** (vous pouvez changer le suffixe en autre chose que **\_form** en utilisant le champ `template_name_suffix` dans votre vue, par exemple `template_name_suffix = '_other_suffix'`).
-<p>Créez le fichier de template <strong>locallibrary/catalog/templates/catalog/author_form.html</strong>, et copiez-y le texte suivant.</p>
+Créez le fichier de template **locallibrary/catalog/templates/catalog/author_form.html**, et copiez-y le texte suivant.
-<pre class="brush: html">{% extends "base_generic.html" %}
+```html
+{% extends "base_generic.html" %}
{% block content %}
-&lt;form action="" method="post"&gt;
+<form action="" method="post">
{% csrf_token %}
- &lt;table&gt;
+ <table>
\{{ form.as_table }}
- &lt;/table&gt;
- &lt;input type="submit" value="Submit" /&gt;
+ </table>
+ <input type="submit" value="Submit" />
-&lt;/form&gt;
-{% endblock %}</pre>
+</form>
+{% endblock %}
+```
-<p>Ce formulaire est semblable à nos formulaires précédents et affiche les champs en utilisant un tableau. Notez aussi comment nous déclarons à nouveau le <code>{% csrf_token %}</code> pour nous assurer que nos formulaires résisteront à d'éventuelles attaques par CSRF (Cross Site Request Forgery).</p>
+Ce formulaire est semblable à nos formulaires précédents et affiche les champs en utilisant un tableau. Notez aussi comment nous déclarons à nouveau le `{% csrf_token %}` pour nous assurer que nos formulaires résisteront à d'éventuelles attaques par CSRF (Cross Site Request Forgery).
-<p>La vue "supprimer" s'attend à trouver un template avec un nom au format <em>model_name</em><strong>_confirm_delete.html</strong> (de nouveau, vous pouvez changer le suffixe en utilisant <code>template_name_suffix</code> dans votre vue). Créez le fichier de template <strong>locallibrary/catalog/templates/catalog/author_confirm_delete</strong><strong>.html</strong>, et copiez-y le texte suivant.</p>
+La vue "supprimer" s'attend à trouver un template avec un nom au format \*model_name**\*\_confirm_delete.html** (de nouveau, vous pouvez changer le suffixe en utilisant `template_name_suffix` dans votre vue). Créez le fichier de template **locallibrary/catalog/templates/catalog/author_confirm_delete\*\***.html\*\*, et copiez-y le texte suivant.
-<pre class="brush: html">{% extends "base_generic.html" %}
+```html
+{% extends "base_generic.html" %}
{% block content %}
-&lt;h1&gt;Delete Author&lt;/h1&gt;
+<h1>Delete Author</h1>
-&lt;p&gt;Are you sure you want to delete the author: \{{ author }}?&lt;/p&gt;
+<p>Are you sure you want to delete the author: \{{ author }}?</p>
-&lt;form action="" method="POST"&gt;
+<form action="" method="POST">
{% csrf_token %}
- &lt;input type="submit" action="" value="Yes, delete." /&gt;
-&lt;/form&gt;
+ <input type="submit" action="" value="Yes, delete." />
+</form>
{% endblock %}
-</pre>
+```
-<h3 id="Configurations_dURL">Configurations d'URL</h3>
+### Configurations d'URL
-<p>Ouvrez votre fichier de configuration d'URL (<strong>locallibrary/catalog/urls.py</strong>) et ajoutez-y à la fin la configuration suivante :</p>
+Ouvrez votre fichier de configuration d'URL (**locallibrary/catalog/urls.py**) et ajoutez-y à la fin la configuration suivante :
-<pre class="brush: python">urlpatterns += [
+```python
+urlpatterns += [
url(r'^author/create/$', views.AuthorCreate.as_view(), name='author_create'),
- url(r'^author/(?P&lt;pk&gt;\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'),
- url(r'^author/(?P&lt;pk&gt;\d+)/delete/$', views.AuthorDelete.as_view(), name='author_delete'),
-]</pre>
-
-<p>Il n'y a rien de particulièrement nouveau ici ! Vous pouvez voir que les vues sont des classes, et doivent dès lors être appelée via <code>.as_view()</code>, et vous devriez être capable de reconnaître les patterns d'URL dans chaque cas. Nous devons utiliser <code>pk</code> comme nom pour la valeur de nos clés primaires capturées, car c'est le nom de paramètre attendu par les classes de vue.</p>
+ url(r'^author/(?P<pk>\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'),
+ url(r'^author/(?P<pk>\d+)/delete/$', views.AuthorDelete.as_view(), name='author_delete'),
+]
+```
-<p>Les pages de création, modification et suppression d'auteur sont maintenant prêtes à être testées (nous ne nous mettons pas en peine pour cette fois, bien que vous puissiez le faire si vous le souhaiter, de les accrocher dans la barre latérale du site).</p>
+Il n'y a rien de particulièrement nouveau ici ! Vous pouvez voir que les vues sont des classes, et doivent dès lors être appelée via `.as_view()`, et vous devriez être capable de reconnaître les patterns d'URL dans chaque cas. Nous devons utiliser `pk` comme nom pour la valeur de nos clés primaires capturées, car c'est le nom de paramètre attendu par les classes de vue.
-<div class="note">
-<p><strong>Note :</strong> Les utilisateurs observateurs auront remarqué que nous n'avons rien fait pour empêcher les utilisateurs non autorisés d'accéder ces pages ! Nous laissons cela comme exercice pour vous (suggestion : vous pourriez utiliser le <code>PermissionRequiredMixin</code>, et soit créer une nouvelle permission, soit réutiliser notre permission<code>can_mark_returned</code> ).</p>
-</div>
+Les pages de création, modification et suppression d'auteur sont maintenant prêtes à être testées (nous ne nous mettons pas en peine pour cette fois, bien que vous puissiez le faire si vous le souhaiter, de les accrocher dans la barre latérale du site).
-<h3 id="Test_de_la_page">Test de la page</h3>
+> **Note :** Les utilisateurs observateurs auront remarqué que nous n'avons rien fait pour empêcher les utilisateurs non autorisés d'accéder ces pages ! Nous laissons cela comme exercice pour vous (suggestion : vous pourriez utiliser le `PermissionRequiredMixin`, et soit créer une nouvelle permission, soit réutiliser notre permission`can_mark_returned` ).
-<p>Tout d'abord, connectez-vous au site avec un compte ayant les permissions que vous avez définies comme nécessaires pour accéder aux pages d'édition d'auteur.</p>
+### Test de la page
-<p>Ensuite naviguez à la page de création d'auteur : <a class="external" href="http://127.0.0.1:8000/catalog/author/create/" rel="noopener">http://127.0.0.1:8000/catalog/author/create/</a>, ce qui devrait ressembler à la capture d'écran ci-dessous.</p>
+Tout d'abord, connectez-vous au site avec un compte ayant les permissions que vous avez définies comme nécessaires pour accéder aux pages d'édition d'auteur.
-<p><img alt="Form Example: Create Author" src="forms_example_create_author.png"></p>
+Ensuite naviguez à la page de création d'auteur : <http://127.0.0.1:8000/catalog/author/create/>, ce qui devrait ressembler à la capture d'écran ci-dessous.
-<p>Entrez des valeurs pour les champs et ensuite cliquez sur <strong>Submit</strong> pour sauvegarder l'enregistrement de cet auteur. Vous devriez maintenant être conduit à une vue "détail" pour votre nouvel auteur, avec une URL du genre <em>http://127.0.0.1:8000/catalog/author/10</em>.</p>
+![Form Example: Create Author](forms_example_create_author.png)
-<p>Vous pouvez tester l'édition d'un enregistrement en ajoutant <em>/update/</em> à la fin de l'URL "détail" (par exemple <em>http://127.0.0.1:8000/catalog/author/10/update/</em>). Nous ne mettons pas de capture d'écran, car c'est à peu près la même chose que la page "create".</p>
+Entrez des valeurs pour les champs et ensuite cliquez sur **Submit** pour sauvegarder l'enregistrement de cet auteur. Vous devriez maintenant être conduit à une vue "détail" pour votre nouvel auteur, avec une URL du genre _http\://127.0.0.1:8000/catalog/author/10_.
-<p>Enfin, nous pouvons effacer l'enregistrement en ajoutant "delete" à la fin de l'URL de détail (par exemple <em>http://127.0.0.1:8000/catalog/author/10/delete/</em>). Django devrait vous afficher la page de suppression montrée ci-dessous. Cliquez sur "<strong>Yes, delete</strong>" pour supprimer l'enregistrement et être reconduit à la liste des auteurs.</p>
+Vous pouvez tester l'édition d'un enregistrement en ajoutant */update/* à la fin de l'URL "détail" (par exemple _http\://127.0.0.1:8000/catalog/author/10/update/_). Nous ne mettons pas de capture d'écran, car c'est à peu près la même chose que la page "create".
-<p><img alt="" src="forms_example_delete_author.png"></p>
+Enfin, nous pouvons effacer l'enregistrement en ajoutant "delete" à la fin de l'URL de détail (par exemple _http\://127.0.0.1:8000/catalog/author/10/delete/_). Django devrait vous afficher la page de suppression montrée ci-dessous. Cliquez sur "**Yes, delete**" pour supprimer l'enregistrement et être reconduit à la liste des auteurs.
-<h2 id="Mettez-vous_au_défi">Mettez-vous au défi</h2>
+![](forms_example_delete_author.png)
-<p>Créez des formulaires pour créer, modifier et effacer des enregistrements de type <code>Book</code>. Vous pouvez utiliser exactement la même structure que pour les <code>Authors</code>. Si votre template <strong>book_form.html</strong> est simplement copié-renommé à partir du template <strong>author_form.html</strong>, alors la nouvelle page "create book" va ressembler à quelque chose comme ceci :</p>
+## Mettez-vous au défi
-<p><img alt="" src="forms_example_create_book.png"></p>
+Créez des formulaires pour créer, modifier et effacer des enregistrements de type `Book`. Vous pouvez utiliser exactement la même structure que pour les `Authors`. Si votre template **book_form.html** est simplement copié-renommé à partir du template **author_form.html**, alors la nouvelle page "create book" va ressembler à quelque chose comme ceci :
-<ul>
-</ul>
+![](forms_example_create_book.png)
-<h2 id="Résumé">Résumé</h2>
+## Résumé
-<p>Créer et gérer des formulaires peut être un processus compliqué ! Django le rend bien plus aisé en fournissant des mécanismes de programmation pour déclarer, rendre et valider des formulaires. Django fournit de plus des vues génériques d'édition de formulaires, qui peuvent faire presque tout le travail si vous voulez définir des pages pour créer, modifier et supprimer des enregistrements associés à une instance d'un modèle unique.</p>
+Créer et gérer des formulaires peut être un processus compliqué ! Django le rend bien plus aisé en fournissant des mécanismes de programmation pour déclarer, rendre et valider des formulaires. Django fournit de plus des vues génériques d'édition de formulaires, qui peuvent faire presque tout le travail si vous voulez définir des pages pour créer, modifier et supprimer des enregistrements associés à une instance d'un modèle unique.
-<p>Il y a bien d'autres choses qui peuvent être faites avec les formulaires (regardez notre liste <a href="/fr/docs/Learn/Server-side/Django/Forms#See_also">See also</a> ci-dessous), mais vous devez être maintenant en mesure de comprendre comment ajouter des formulaires basiques et un code de gestion de formulaire à vos propres sites web.</p>
+Il y a bien d'autres choses qui peuvent être faites avec les formulaires (regardez notre liste [See also](/fr/docs/Learn/Server-side/Django/Forms#See_also) ci-dessous), mais vous devez être maintenant en mesure de comprendre comment ajouter des formulaires basiques et un code de gestion de formulaire à vos propres sites web.
-<h2 id="See_also">See also</h2>
+## See also
-<ul>
- <li><a href="https://docs.djangoproject.com/en/1.10/topics/forms/">Working with forms</a> (Django docs)</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial04/#write-a-simple-form">Writing your first Django app, part 4 &gt; Writing a simple form</a> (Django docs)</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/api/">The Forms API</a> (Django docs)</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/">Form fields</a> (Django docs) </li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/validation/">Form and field validation</a> (Django docs)</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-editing/">Form handling with class-based views</a> (Django docs)</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/">Creating forms from models</a> (Django docs)</li>
- <li><a href="https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-editing/">Generic editing views</a> (Django docs)</li>
-</ul>
+- [Working with forms](https://docs.djangoproject.com/en/1.10/topics/forms/) (Django docs)
+- [Writing your first Django app, part 4 > Writing a simple form](https://docs.djangoproject.com/en/1.10/intro/tutorial04/#write-a-simple-form) (Django docs)
+- [The Forms API](https://docs.djangoproject.com/en/1.10/ref/forms/api/) (Django docs)
+- [Form fields](https://docs.djangoproject.com/en/1.10/ref/forms/fields/) (Django docs)
+- [Form and field validation](https://docs.djangoproject.com/en/1.10/ref/forms/validation/) (Django docs)
+- [Form handling with class-based views](https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-editing/) (Django docs)
+- [Creating forms from models](https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/) (Django docs)
+- [Generic editing views](https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-editing/) (Django docs)
-<p>{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</p>
+{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}