aboutsummaryrefslogtreecommitdiff
path: root/files/fr/learn/server-side/django
diff options
context:
space:
mode:
Diffstat (limited to 'files/fr/learn/server-side/django')
-rw-r--r--files/fr/learn/server-side/django/admin_site/index.html371
-rw-r--r--files/fr/learn/server-side/django/development_environment/index.html423
-rw-r--r--files/fr/learn/server-side/django/forms/index.html651
-rw-r--r--files/fr/learn/server-side/django/home_page/index.html429
-rw-r--r--files/fr/learn/server-side/django/index.html65
-rw-r--r--files/fr/learn/server-side/django/introduction/index.html277
-rw-r--r--files/fr/learn/server-side/django/models/index.html468
-rw-r--r--files/fr/learn/server-side/django/skeleton_website/index.html403
-rw-r--r--files/fr/learn/server-side/django/testing/index.html956
-rw-r--r--files/fr/learn/server-side/django/tutorial_local_library_website/index.html101
-rw-r--r--files/fr/learn/server-side/django/vues_generiques/index.html634
11 files changed, 4778 insertions, 0 deletions
diff --git a/files/fr/learn/server-side/django/admin_site/index.html b/files/fr/learn/server-side/django/admin_site/index.html
new file mode 100644
index 0000000000..1485d7e8a1
--- /dev/null
+++ b/files/fr/learn/server-side/django/admin_site/index.html
@@ -0,0 +1,371 @@
+---
+title: 'Django didactique Section 4: Site d''administration de Django'
+slug: Learn/Server-side/Django/Admin_site
+tags:
+ - Apprentissage
+ - Article
+ - Didacticiel
+ - Débutant
+ - Python
+ - django
+ - django_admin
+translation_of: Learn/Server-side/Django/Admin_site
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Nous avons créé le modèle de données pour le site web de la <a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">bibliothèque locale</a>. Dans ce chapitre nous allons utiliser le site d'administration pour introduire des données réelles pour les livres. Dans un premier temps, nous aborderons la manière d'enregistrer les données des objets sur le site d'administration et comment se connecter au site et créer des données. La fin de ce chapitre sera dédié à des éléments d'amélioration possible du site d'administration.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pré-requis:</th>
+ <td>Avoir complété <a href="/fr/docs/Learn/Server-side/Django/Models">Django didactique Section 3: Utilisation des modèles de données</a></td>
+ </tr>
+ <tr>
+ <th scope="row">Objectif:</th>
+ <td>Comprendre les avantages et les limites du site d'administration de Django. Utiliser ce site pour enregistrer des données pour les objets créés dans le chapitre précédent.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Survol">Survol</h2>
+
+<p>Le site d'administration et l'application admin associée de Django peut utiliser les objets déclarés du modèle de données pour réaliser automatiquement un espace de publications, de création, de mise à jour ou de suppression d'enregistrements. Cet outil permet d'économiser du temps pendant les développements et de tester rapidement le modèle de données et par voie de conséquence de vérifier la disponibilité des données et la cohérence du modèle créé. En fonction de votre type d'application web, le site d'administration peut aussi servir à gérer les données du site en production. Comme une approche centrée sur le modèle de données n'est pas appropriée à une présentation utilisateur, les concepteurs de Django recommandent de ne se servir de ce site que pour une administration interne des données (c'est-à-dire, juste pour les administrateurs techniques ou fonctionnels de l'application).</p>
+
+<p>Quand nous avons créé <a href="/fr/docs//Learn/Server-side/Django/skeleton_website">le squelette du projet</a>, nous avons généré automatiquement toute ce qui était nécessaire à son administration au sein de l'application web (<a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/">le détail des relations en jeux</a> sont décrites sur le site documentaire Django). Au final, vous n'aurez juste qu'à ajouter vos modèles dans l'administration du site en les enregistrant. A la fin de ce chapitre, vous aurez des pistes sur l'une des manière d'améliorer l'affichage des données dans la zone d'administration.</p>
+
+<p>Passons aux actes ! Après l'enregsitrment des objets du modèle relationnel, nous verrons comment créer un super-utilisateur, s'authetifier et ensuite créer quelques livres, auteurs et ouvrages à la dispostion des lecteurs. Ces données seront très utiles pour tester ensuite les vues et gabarits qui seront abordés dans les chapitres suivants.</p>
+
+<h2 id="Enregistrer_les_objets_de_la_modélisation">Enregistrer les objets de la modélisation</h2>
+
+<p>En premier lieu, il faut editer le fichier <strong>admin.py</strong> de l'application catalog (c'est-à-dire le fichier <strong>./locallibrary/catalog/admin.py</strong>). Il devrait ressembler à celui ci-dessous — notez qu'il contient d'ores et déjà l'import du module <code>django.contrib.admin</code>:</p>
+
+<pre class="brush: python">from django.contrib import admin
+
+# Register your models here.
+</pre>
+
+<p>L'enregistrement de objets de modélisation ce fait par l'appel de la fonction <code>admin.site.register</code> comme indiqué ci-dessous. Il vous suffit pour le moment de copier le texte ci-dessous et de l'ajouter à la fin du fichier.Register the models by copying the following text into the bottom of the file.</p>
+
+<pre class="brush: python">from catalog.models import Author, Genre, Book, BookInstance
+
+admin.site.register(Book)
+admin.site.register(Author)
+admin.site.register(Genre)
+admin.site.register(BookInstance)
+</pre>
+
+<div class="note"><strong>Note</strong>: Si vous avez répondu au défi de la modelisation des langues des livres (<a href="/fr/docs/Learn/Server-side/Django/Models">voir le chapitre précédent sur les modèles de données</a>), vous pouvez aussi importer cet objet !<br>
+<br>
+Cela devrait être de la forme : <code>admin.site.register(Language)</code> et n'oubliez pas d'importer l'objet.</div>
+
+<p>C'est la méthode la plus rapide et la plus simple pour enregistrer un ou plusieurs modèles. Le site d'administration est très adaptable et nous aborderons plus loin ces questions.</p>
+
+<h2 id="Générer_un_super-utilisateur">Générer un super-utilisateur</h2>
+
+<p>Pour acceder au site d'administration, il est necessaire de s'authentifier avec un utilisateur qui dispose du statut <em>Statut d'équipe</em> activé. Afin de visualiser et créer des enregsitrement, vous aurez aussi besoin de disposer de droits de manipulation des obejts. A ce stade, vous pouvez créer à l'aide du fichier <strong>manage.py</strong> un super-utilisateur qui dispose de tous les droits et permissions.</p>
+
+<p>Exécutez la commande python ci-dessous qui appelle le fichier <strong>manage.py</strong> en étant dans le même dossier que le fichier (c'est-à-dire <strong>./locallibrary/</strong>), pour créer le super-utilsiateur. La commande va vous demander de répondre le nom d'utilsiateur, l'adresse mail et un mot de passe fort.</p>
+
+<pre class="brush: bash">python3 manage.py createsuperuser
+</pre>
+
+<p>Une fois cette étape réalisée, vous pouvez redémarrer le serveur de développement :</p>
+
+<pre class="brush: bash">python3 manage.py runserver
+
+</pre>
+
+<h2 id="Accéder_et_utiliser_le_site_admin">Accéder et utiliser le site admin</h2>
+
+<p>Pour vous authentifier au site, ouvrez l'URL <em>/admin </em>du site local (concrètement, <a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin</a>) et identifiez vous avec votre compte de super-utilisateur.</p>
+
+<div class="blockIndicator note">
+<p>Vous serez redirigez vers l'application interne à Django de gestion de l'authentification et la pages de demande d'authentitification avant d'accéder réellement au site d'administration.</p>
+
+<p>Si vous accéder au site local sans /admin, vous aurez un message d'erreur car les routages d'URL n'ont pas encore été traité. ne vous en inquiétez pas cela va venir...</p>
+</div>
+
+<p>Cet partie du site affiche tous les modèles définis et déclarés dans le fichier de contrôle de l'administration du site. Les objets sont regroupés par application (pour notre cas, uniquement l'application Catalog à cette étape des travaux). Vous pouvez cliquez sur chacun des noms d'objet publiés pour accéder à l'écran qui gère les informations sur les objets de ce type contenu en base de données et vous pouvez les éditer et les modifier. Vous pouvez aussi cliquer sur le lien <strong>+ Ajouter</strong> pour créer un nouvel enregistrement.</p>
+
+<p><img alt="Admin Site - Home page" src="https://mdn.mozillademos.org/files/13975/admin_home.png" style="display: block; height: 634px; margin: 0px auto; width: 998px;"></p>
+
+<p>Cliquez sur le lien <strong>+ Ajouter</strong> à la droite de l'objet Books pour créer un nouveau livre. Le site va afficher une page de saisie de données (analogue à celle ci-dessous). Notez que Django prend en compte le type de champs définit dans le modèle pour utiliser le widget associé ainsi que le champs <code>help_text</code> quand vous l'aviez défini. </p>
+
+<p>Entrez les valeurs des champs. Pour les champs qui relève de relations entre objet, vous pouvez utiliser le bouton + pour accéder en cascade au formulkaire de saisie des informations nécessaires à la créarion de cette objet. Vous pouvez aussi sélectionner un objet si d'autres avaient été créés précédement. Ne pas oublier de cliquer sur <strong>Enregistrer et ajouter un nouveau</strong> ou <strong>Enregistrer et continuer les modification</strong> pour sauvegarder en base de données les informations saisies.</p>
+
+<p><img alt="Admin Site - Book Add" src="https://mdn.mozillademos.org/files/13979/admin_book_add.png" style="border-style: solid; border-width: 1px; display: block; height: 780px; margin: 0px auto; width: 841px;"></p>
+
+<div class="note">
+<p><strong>Note</strong>: À ce stade, prenez le temps d'enregistrer plusieurs livres, genres et auteurs. Assurez-vous que chacun est associé à plusieurs autres éléments cela rendra vos listes à venir plus riches et intéressantes quand nous aborderons ces sujets.</p>
+</div>
+
+<p>Après avoir saisie les informations et ajouté vos livres, cliquez sur le lien <strong>Accueil</strong> pour revenir à la page principale du site d'administration. Cliquez sur le lien <strong>Books</strong> pour afficher la liste des livres enregistrés (ou sur d'autres liens pour voir les autres objets présents en base). Après avoir ajouter quelques livres, votre page devrait ressembler à celle ci-dessous. La liste des livres est affichée par titre ; c'est, en fait, la valeur délivrée par la méthode <code>__str__()</code> du modèle d'objet Book comme cela a été codé dans le précédent chapitre.</p>
+
+<p><img alt="Admin Site - List of book objects" src="https://mdn.mozillademos.org/files/13935/admin_book_list.png" style="border-style: solid; border-width: 1px; display: block; height: 407px; margin: 0px auto; width: 1000px;"></p>
+
+<p>À partir de la liste affichée, vous pouvez supprimer des instances en selectionnant les items par les cases à cocher à gauche du titre puis <em>supprimer...</em> dans la liste des actions proposée puis en cliquant sur <strong>Envoyer</strong>. Vous pouvez aussi ajouter des livres en cliquant sur <strong>AJOUTER BOOK</strong>.</p>
+
+<p>Vous pouvez editer un livre en cliquant son nom sur la liste des ouvrages. La page d'édition, image ci-dessous, est proche de celle d'ajout d'un livre. Les principales différences sont le titre de la page (Modification de book, au lieu d'ajout de bbok), l'ajout en rouge du bouton supprimer, l'historique des modifications et voir sur le site. Ce dernier bouton est visible car nous créer la méthode <code>get_absolute_url()</code> dans la définition du modèle de données (à ce stade, une erreur sera provoquée si vous cliquez sur ce bouton).</p>
+
+<p><img alt="Admin Site - Book Edit" src="https://mdn.mozillademos.org/files/13977/admin_book_modify.png" style="border-style: solid; border-width: 1px; display: block; height: 780px; margin: 0px auto; width: 841px;"></p>
+
+<p>Revenez à la page d'accueil (à l'aide du lien <strong>Accueil</strong> du fil d'Ariane), puis affichez les listes des <strong>Authors</strong> et des <strong>Genres</strong>. Vous devriez déjà en avoir créé un certain nombre à partir de l'ajout des nouveaux livres, mais n'hésitez pas à en ajouter d'autres.</p>
+
+<p>Ce qui manque actuellement ce sont des <em>Book Instances</em>. Vous n'en avez pas car elles ne sont pas créées à partir des objets Books (bien que vous pourriez créer un objet <code>Book</code> à partir d'un objet <code>BookInstance</code> car c'est la nature de la relation <code>ForeignKey</code>). Retournez à la page d'acceuil et cliquez sur le bouton <strong>Ajouter</strong> associé aux objets Book Instance et accéder à l'écran de création. Vous pouvez noter le très grand identifiant unique global utilisé pour identifier séparelment les ouvrages.</p>
+
+<p><img alt="Admin Site - BookInstance Add" src="https://mdn.mozillademos.org/files/13981/admin_bookinstance_add.png" style="border-style: solid; border-width: 1px; display: block; height: 514px; margin: 0px auto; width: 863px;"></p>
+
+<p>Créez plusieurs de ces enregistrements pour chacun de vos livres. Définissez un statut <strong>Available</strong> (<em>Disponible</em>) pour certains d'entre eux et <strong>On loan</strong> (<em>Prêt</em>) pour d’autres. Pour un statut différent de <em>Available</em>, vous devrez préciser une date d'échéance à venir.</p>
+
+<p>Nous avons terminé cette étape ! Vous savez comment configurer et utiliser le site d'administration. Vous pouvez continuer à créer des enregistrements pour Book, BookInstance, Genre et Author, que nous pourrons utiliser une fois que nous aurons créé nos propres vues de détail.</p>
+
+<h2 id="Configuration_avancée">Configuration avancée</h2>
+
+<p>La cadriciel Django réalise une excellente assistance avec la création d'un site d'administration debase en utilisant les données des enregistrements effectués :</p>
+
+<ul>
+ <li>Pour chaque modèles, les enregistrement sont identifiés par le résultat de la méthode<code> __str__()</code>, et les détails sont accessible par des vues dédiées. Par défaut, ces vues et formulaires dispose d'un menu en haut et vous pouvez opérer des opérations de suppressions en bloc en sélectionnant les enregistrements.</li>
+ <li>Le détail de chaque modèle est contenu dans un formulaire où chaque champ est affiché verticalement dans l'ordre de déclaration de ces derniers dans le modèle d'objet. </li>
+</ul>
+
+<p>mais vous avez la possibilité de personnaliser le comportement du site d'administration. Vous allez pouvoir notamment faire :</p>
+
+<ul>
+ <li>Des vues en liste
+ <ul>
+ <li>Ajouter des champs ou des informations supplémentaires affichés pour chaque enregistrement.</li>
+ <li>Ajouter des filtres pour sélectionner les enregistrements répertoriés, en fonction de la date ou d’une autre valeur de sélection (par exemple, le statut du prêt du livre).</li>
+ <li>Ajouter des options supplémentaires au menu Actions dans les vues de liste et choisir l'emplacement où ce menu est affiché dans le formulaire.</li>
+ </ul>
+ </li>
+ <li><span class="tlid-translation translation" lang="fr"><span title="">Vues détaillées</span></span>
+ <ul>
+ <li><span class="tlid-translation translation" lang="fr"><span title="">Choisir les champs à afficher (ou à exclure), ainsi que leur ordre, leur groupement, leur caractère modifiable, le widget utilisé, leur orientation, etc.</span></span></li>
+ <li><span class="tlid-translation translation" lang="fr"><span title="">Ajouter des champs associés à un enregistrement pour permettre la modification en ligne (par exemple, ajoutez la possibilité d'ajouter et de modifier des enregistrements de livre lors de la création de leur auteur).</span></span></li>
+ </ul>
+ </li>
+</ul>
+
+<p>Dans la section qui suit, nous allons effectuer quelques modification pour améliorer l'interface de votre application <em>LocalLibrary</em>. Nous allons notamment ajouter des information pour les objets <code>Book</code> et <code>Author</code>, et améliorer la présentation de leur vue d'édition. Il n'y aura pas de changement pour les objets <code>Language</code> et <code>Genre</code> qui ne possède pas assez d'information pour que cela puisse avoir une incidence réelle !</p>
+
+<p>Le détail complet de la personnalisation du site d'administration est disponible <a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/">sur le site documentaire de Django</a>.</p>
+
+<h3 id="Enregistrer_un_objet_de_la_classe_ModelAdmin">Enregistrer un objet de la classe ModelAdmin</h3>
+
+<p>Pour modifier la manière d'afficher un objet hérité de la classe Model dans l'interface d'administration, vous devez définir une classe d'objet héritée de la classe ModelAdmin qui décrit l'affichage d'un objet et de l'enregistrer avec votre objet Model.</p>
+
+<p>Commençons avec l'objet <code>Author</code>. Éditez le fichier <strong>admin.py</strong> dans le dossier catalog de l'application(concrètement le fichier <strong>/locallibrary/catalog/admin.py</strong>). Commentez la ligne qui vous a permis d'enregistrer l'objet <code>Author</code> :</p>
+
+<pre class="brush: js"># admin.site.register(Author)</pre>
+
+<p>Ensuite ajoutez une nouvelle classe d'objets <code>AuthorAdmin</code> et enregistrez-le comme indiqué ci-dessous.</p>
+
+<pre class="brush: python"># Define the admin class
+class AuthorAdmin(admin.ModelAdmin):
+ pass
+
+# Register the admin class with the associated model
+admin.site.register(Author, AuthorAdmin)
+</pre>
+
+<p>Ensuite nous allons opérer de manière analogue avec un objet hérité de <code>ModelAdmin</code> pour les objets <code>Book</code>, et <code>BookInstance</code>. À nouveau, nous commentons les enregistrements initiaux :</p>
+
+<pre class="brush: js"># admin.site.register(Book)
+# admin.site.register(BookInstance)</pre>
+
+<p>Puis nous créons et enrgistrons les nouveaux modèles. Pour les besoins de l'exercice, nous allons utiliser, pour enregistrer ces modèles, le décorateur <code>@register</code> qui réalise la même opération que la méthode <code>admin.site.register()</code> :</p>
+
+<pre class="brush: python"># Register the Admin classes for Book using the decorator
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ pass
+
+# Register the Admin classes for BookInstance using the decorator
+@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+ pass
+</pre>
+
+<p>Pour le moment, toutes les classes d’administration sont vides (cf. pass), par conséquent le comportement d'affichage n'est pas modifié. Cependant, nous allons pouvoir désormais modifier les comportements d'affichage pour chacun des objets nouvellements enregistrés.</p>
+
+<h3 id="Configurer_les_vues_en_liste">Configurer les vues en liste</h3>
+
+<p>La liste des auteurs (objet <code>Author</code>) est affiché dans l'application <em>LocalLibrary</em> à l'aide du nom généré par la méthode  <code>__str__()</code>. Ceci fonctionne bien, judqu'à ce que vous aurez de nombreux auteurs et éventuellement des doublons parmi ces auteurs. Pour bien les différencier, ou simplement parce que vous souhaitez avoir directement plus d'informations, vous allez utiliser la directive <a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin">list_display</a> tpour ajouter d'autres champs de l'objet <code>Author</code>.</p>
+
+<p><span class="tlid-translation translation" lang="fr"><span title="">Modifiez votre classe <code>AuthorAdmin</code> comme décrit ci-dessous (vous pouvez copier et coller le code).</span> <span title="">Les noms de champs à afficher dans la liste sont déclarés dans un tuple dans l'ordre requis. Ils sont identiques à </span></span><span class="tlid-translation translation" lang="fr"><span title="">ceux spécifiés dans votre modèle d'objet <code>Author</code>.</span></span></p>
+
+<pre class="brush: python">class AuthorAdmin(admin.ModelAdmin):
+ list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+</pre>
+
+<p>Si vous accèdez à la page d'administration des auteurs, vous devriez obtenir une page équivalente à celle ci-dessous :</p>
+
+<p><img alt="Admin Site - Improved Author List" src="https://mdn.mozillademos.org/files/14023/admin_improved_author_list.png" style="border-style: solid; border-width: 1px; display: block; height: 302px; margin: 0px auto; width: 941px;"></p>
+
+<p>Pour les livres, nous allons visulaiser les objets  <code>Book</code> en affichant les champs <code>author</code> and <code>genre</code>. Le champs <code>author</code> est de type <code>ForeignKey</code> décrivant une relation un à n. En conséquence, nous afficherons l'élément produit par la méthode <code>__str__()</code> de l'objet <code>Author</code> pour l'instance associée à votre livre. Le genre est une relation n à n, donc nous allons avoir à traiter son affichage de manière particulière. Modifiez la classe <code>BookAdmin</code> comme suit :</p>
+
+<pre class="brush: python">class BookAdmin(admin.ModelAdmin):
+ list_display = ('title', 'author', 'display_genre')
+</pre>
+
+<p>Le champ <font face="Consolas, Liberation Mono, Courier, monospace">genre </font>représente une relation n à n (<code>ManyToManyField</code>)qui ne peut pas être prise en charge par la directive <code>list_display</code>. Le coût d'accès à la base de donnée peut être important et donc le cadriciel se protège de ce phénomène. A la place, nous devons définir une fonction(<code>display_genre</code>) qui permettra de traiter l'affichage des informations souhaitées pour le genre.</p>
+
+<div class="note">
+<p><strong>Note</strong>: C'est dans un but pédagogique que nous recherchons ici l'affichage du <code>genre</code> qui n'a peut-être pas nécessaire d'intérêt et peut représenter un coût d'accès. Nous montrons, ici, comment appler les fonctions dans vos modèles ce qui sera très utile pour la suite de vos applications  — par exemple pour ajouter un lien de suppression de vos enregistrements en liste.</p>
+</div>
+
+<p>Ajoutez le code ci-dessous dans votre modèle d'objet <code>Book</code> (concrètement dans le fichier <strong>locallibrary/catalog/models.py</strong>). Cette fonction génère une chaîne de caractère contenant les trois premières valeurs de tous les genres (s'ils existent) et créer une courte destription (<code>short_description</code>) qui sera utilisé par le site d'administration avec cette méthode.</p>
+
+<pre class="brush: python">    def display_genre(self):
+ """Create a string for the Genre. This is required to display genre in Admin."""
+ return ', '.join(genre.name for genre in self.genre.all()[:3])
+
+ display_genre.short_description = 'Genre'
+</pre>
+
+<p>Après avoir sauvegardé vos fichiers models.py et admin.py, vous pouvez accéder à la page web d'administration des livres et vous y découvrirez une page semblable à celle ci-dessous :</p>
+
+<p><img alt="Admin Site - Improved Book List" src="https://mdn.mozillademos.org/files/14025/admin_improved_book_list.png" style="border-style: solid; border-width: 1px; display: block; height: 337px; margin: 0px auto; width: 947px;"></p>
+
+<p>Les champs <code>Genre</code> <code>Language</code> ne dispose que d'une seule valeur. Il n'est donc pas utile de créer une page d'affichage spélicale.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Vous trouverez en fin d'article dans la défis personnel des propositions pour améliorer les ouvrages en prêt <code>BookInstance</code> !</p>
+</div>
+
+<h3 id="Ajouter_des_filtres">Ajouter des filtres</h3>
+
+<p>Si vous avez beaucoup d'éléments à l'affichage des listes, il devient utile de d'appliquer des filtres pour les afficher. Ceci est réalisé avec l'attribut <code>list_filter</code> de la classe ModelAdmin. Modifier votre classe d'objet d'affichage <code>BookInstanceAdmin</code> avec les code ci-dessous :</p>
+
+<pre class="brush: python">class BookInstanceAdmin(admin.ModelAdmin):
+<strong> list_filter = ('status', 'due_back')</strong>
+</pre>
+
+<p>La page de la vue en liste des ouvrages à consultation (BookInstance) est désormais agrémentée d'un bloc de filtrage par statut (champs status) et date de retour (due back). Vous pouvez sélectionner la valeur de ces deux critères de filtrage (remarquez la manière avec laquelle les valeurs des critères est proposée).</p>
+
+<p><img alt="Admin Site - BookInstance List Filters" src="https://mdn.mozillademos.org/files/14037/admin_improved_bookinstance_list_filters.png" style="height: 528px; width: 960px;"></p>
+
+<h3 id="Organiser_la_vue_d'affichage_d'un_modèle">Organiser la vue d'affichage d'un modèle</h3>
+
+<p>La vue est agencée, par défaut, en affichant verticalement dans l'ordre de déclaration des champs de l'objet modèle. Cette règle d'affichage peut être modifiée en indiquant quels champs afficher (ou exclure) et organiser les informations en sections avec un affichage horizontal ou vertical et les widgets à utiliser.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Les modèles de l'application <em>LocalLibrary</em> ne sont pas très compliqués sans énormément d'information à traiter. Il n'y a pas un grand besoin de changement d'affichage ; les éléments ci-dessous sont données pour avoir une idée des possibilités et savoir, le moment venu, comment faire.</p>
+</div>
+
+<h4 id="Contrôler_l'affichage_et_la_dispostion_des_champs">Contrôler l'affichage et la dispostion des champs</h4>
+
+<p>Modifiez votre classe d'objet <code>AuthorAdmin</code> en ajoutant l'attribut <code>fields</code> comme indiqué en gras ci-dessous :</p>
+
+<pre class="brush: python">class AuthorAdmin(admin.ModelAdmin):
+ list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+<strong> fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]</strong>
+</pre>
+
+<p>Ce champ (<code>fields</code>) contrôle l'affichage des champs. Les champs déclarés sont affichés verticalement dans l'ordre de déclaration et seront affichés en groupe horizontalement s'ils sont déclarés dans un tuple (c'est le cas pour les date de naissance et de décès des auteurs).</p>
+
+<p>La page web de votre application locale devrait ressembler à celle ci-dessous :</p>
+
+<p><img alt="Admin Site - Improved Author Detail" src="https://mdn.mozillademos.org/files/14027/admin_improved_author_detail.png" style="border-style: solid; border-width: 1px; display: block; height: 282px; margin: 0px auto; width: 928px;"></p>
+
+<div class="note">
+<p><strong>Note</strong>: Vous pouvez aussi utiliser l'attribut <code>exclude</code> pour identifier des attributs du modèle que vous souhaitez exclure de l'affichage (les autres attributs seront alors affichés). Pour plus de détails vous pouvez consulter la documentation Django sur l'attribut <a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.exclude">exclude</a>.</p>
+</div>
+
+<h4 id="Organiser_des_sections_dans_votre_vue_de_détail">Organiser des sections dans votre vue de détail</h4>
+
+<p>Vous avez la possibilité de créer des sections à l'affichage pour regrouper des éléments à renseigner en utilisant l'attribut <a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets">fieldsets</a>.</p>
+
+<p>Nous allons utiliser l'objet <code>BookInstance</code> pour mettre en avant cette possibilité. Nous avons à afficher des informations sur l'ouvrage (nom, édition, id) et sur sa disponibilité actuelle ou à venir (statut et retour de prêt). Nous choisissons d'afficher ces éléments dans deux sections différentes, l'une nommée et l'autre pas. Modifiez l'objet BookInstanceAdmin avec le texte en gras comme ci-dessous :</p>
+
+<pre class="brush: python">@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+ list_filter = ('status', 'due_back')
+
+<strong> fieldsets = (
+ (None, {
+ 'fields': ('book', 'imprint', 'id')
+ }),
+ ('Availability', {
+ 'fields': ('status', 'due_back')
+ }),
+ )</strong></pre>
+
+<p>Chaque section peut avoir un titre (ou aucun si vous indiquez la valeur <code>None</code>) et des champs regroupés à l'aide de tuples enregistrés dans un dictionnaire — le schéma de déclaration peut paraître compliqué à décrire mais assez aisé à comprendre à la lecture du code ci-dessus formaté pour être plus compréhensible.</p>
+
+<p>Le résultat de cette description devrait vous apparaître de manière analogue à celle présente  ci-dessous :</p>
+
+<p><img alt="Admin Site - Improved BookInstance Detail with sections" src="https://mdn.mozillademos.org/files/14029/admin_improved_bookinstance_detail_sections.png" style="border-style: solid; border-width: 1px; display: block; height: 580px; margin: 0px auto; width: 947px;"></p>
+
+<h3 id="Publier_des_enregistrements_associés">Publier des enregistrements associés</h3>
+
+<p>Parfois, il peut être très utile d'ajouter à l'affichage des éléments associés en même temps. C'est le cas, par exemple, pour les copies d'ouvrage associés à un livre en bibliothèque. Il est utile pour le bibliothécaire de disposer à la fois des informations sur le livre et des copies présentes ou non en rayonnage..</p>
+
+<p>Pour cela, vous pouvez utiliser un d'objet pour un affichage horizontal (<a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/#django.contrib.admin.TabularInline">TabularInline</a>) ou vertical (<a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/#django.contrib.admin.StackedInline">StackedInline)</a> (qui n'est autre que l'affichage standard des données). Modifiez le code associé à votre modèle <code>BookInstance</code> dans le fichier <strong>admin.py</strong> pour disposer des informations <em>inline</em> à l'affichage des informations sur votre objet <code>Book</code>. Gardez en mémoire que c'est l'objet  <code>BookAdmin</code> qui gère l'affichage les informations de l'objet <code>Book</code>; c'est donc <code>BookAdmin</code> il doit donc être modifié :</p>
+
+<pre class="brush: python"><strong>class BooksInstanceInline(admin.TabularInline):
+    model = BookInstance</strong>
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+    list_display = ('title', 'author', 'display_genre')
+<strong>    inlines = [BooksInstanceInline]</strong>
+</pre>
+
+<p>Si vous allez consulter un livre, vous devriez pouvoir, au bas de la page, consulter la liste des copies enregistrées :</p>
+
+<p><img alt="Admin Site - Book with Inlines" src="https://mdn.mozillademos.org/files/14033/admin_improved_book_detail_inlines.png" style="border-style: solid; border-width: 1px; display: block; height: 889px; margin: 0px auto; width: 937px;"></p>
+
+<p>Dans le cas présent nous avons juste décidé d'afficher toutes les informations des copies associées à un livre. Si vous consultez sur la documentation Django les informations relatives au type <a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/#django.contrib.admin.TabularInline">TabularInline</a> vous aurez accès à l'ensemble des éléments qui permettent de filtrer et afficher les éléments dont vous aurez besoin. </p>
+
+<div class="note">
+<p><strong>Note</strong>: Il y a quelques limitation pénibles à ces outils. Si vous observez bien la liste des copies pour un ouvrage, vous decouvrirez des copies fantômes sans nom et informations pré-reservées pour de futures instances à enregistrer. Il serait préférable de ne pas les avoir et vous devriez alors appliquer un filtre pour éliminer de l'affichage ces copies. Vous pourriez aussi ajouter une section particulière pour permettre d'ajouter de nouvelles copies dans les rayonnages... La première solution est assez rapide à traiter en utilisant l'attribut <code>extra</code> à 0 dans la définition de l'objet <code>BooksInstanceInline</code> ... essayez !</p>
+</div>
+
+<h2 id="Défi">Défi</h2>
+
+<p>Beaucoup de sujets ont été abordés dans ce chapitre, c'est l'occasion de les mettre en application :</p>
+
+<ol>
+ <li>Améliorer l'affichage des objets <code>BookInstance</code>, ajoutez les éléments nécessaire pour disposer du livre, du statut de la date de fin de prêt et de l'identifiant au lieu du code unique et du titre donné par la méthode <code>__str__()</code> de l'objet.</li>
+ <li>Ajouter une information associée pour disposer du détail des informations sur l'auteur. Appuyez vous sur l'exemple avec les objets <code>Book</code>/<code>BookInstance</code> pour y parvenir.</li>
+</ol>
+
+<ul>
+</ul>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Beaucoup de sujets ont été abordés dans ce chapitre... Vous avez acquis les base du site d'administration et à créer un suoper-utilisateur, voius avez aussi navigué dans le site d'admlinistration et vous avez appris à modifier les formulaires de saisie et comment ajouter, modifier ou supprimer des données.</p>
+
+<h2 id="A_voir_aussi">A voir aussi</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/intro/tutorial02/#introducing-the-django-admin">Ecrire sa première application Dajngo, 2ème partie</a>  (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/contrib/admin/">Le site d'administration de Django</a> (Django Docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}</p>
+
+<h2 id="In_this_module">In this module</h2>
+
+<ul>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Didactique: Site web "Bibliothèque locale"</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Django didactique Section 2: Créer le squelette du site web</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Models">Django didactique Section 3: Utilisation des modèles de données</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Admin_site">Django didactique Section 4 : Site d'administration de Django</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Home_page">Django didactique Section 5: Créer la page d'accueil</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a> </li>
+</ul>
diff --git a/files/fr/learn/server-side/django/development_environment/index.html b/files/fr/learn/server-side/django/development_environment/index.html
new file mode 100644
index 0000000000..b47662e6ff
--- /dev/null
+++ b/files/fr/learn/server-side/django/development_environment/index.html
@@ -0,0 +1,423 @@
+---
+title: Mettre en place un environnement de développement Django
+slug: Learn/Server-side/Django/development_environment
+translation_of: Learn/Server-side/Django/development_environment
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Maintenant que vous savez à quoi sert Django, nous allons vous montrer comment mettre en place et tester un environnement de développement Django sous Windows, Linux (Ubuntu) et macOS — Peu importe votre système d'exploitation, cet article devrait vous fournir de quoi commencer à développer des applications Django.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis :</th>
+ <td>Connaissances de base sur l'utilisation d'un terminal/invite de commande et comment installer des packages sur l'OS de l'ordinateur que vous utiliserez pour développer.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objectif :</th>
+ <td>Avoir un environnement de développement pour Django (2.0) fonctionnel sur votre ordinateur.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Aperçu_de_lenvironnement_de_développement_Django">Aperçu de l'environnement de développement Django</h2>
+
+<p>Django simplifie le processus de configuration de votre ordinateur pour que vous puissiez rapidement commencer à développer des applications web. Cette section explique ce que vous aurez dans l'environnement de développement, et vous fournit un aperçu de certaines options de configuration et d'installation. Le reste de l'article explique la méthode <em>recommandée </em>pour installer l'environnement de développement Django sur Ubuntu, macOS et Windows, et comment le tester.</p>
+
+<h3 id="Quest-ce_que_lenvironnement_de_développement_Django">Qu'est-ce que l'environnement de développement Django ?</h3>
+
+<p>L'environnement de développement correspond à une installation de Django sur votre ordinateur local que vous pouvez utiliser pour développer et tester des applications Django avant de les déployer sur un environnement de production.</p>
+
+<p>Le principal outil que fournit Django est un ensemble de scripts Python utilisés pour créer et travailler avec des projets Django, ainsi qu'un simple <em>serveur web de développement </em>que vous pouvez utiliser pour tester en local (i.e. sur votre propre ordinateur, pas sur un serveur web externe) des applications web Django dans votre navigateur web.</p>
+
+<p>Il y a plusieurs autres outils annexes, qui font partie de l'environnement de développement, que nous ne couvrirons pas ici. Cela inclut des choses comme un <a href="/en-US/docs/Learn/Common_questions/Available_text_editors">éditeur de texte</a> ou un IDE pour éditer votre code, et un outil de gestion de contrôle de version comme Git pour gérer en toute prudence les différentes versions de votre code. Nous supposerons ici que vous avez déjà un éditeur de texte installé.</p>
+
+<h3 id="Quelles_sont_les_options_dinstallation_de_Django">Quelles sont les options d'installation de Django ?</h3>
+
+<p>Django est extrêmement flexible sur sa manière d'être installé et configuré. Django peut-être :</p>
+
+<ul>
+ <li>Installé sur divers systèmes d'exploitation.</li>
+ <li>Installé depuis la source, avec l'Index des Packages Python (PyPI) et bien souvent depuis l'application de gestion de packages de l'ordinateur hôte.</li>
+ <li>Configuré pour communiquer avec diverses bases de données, qui peuvent aussi avoir besoin d'être configurées et installées séparément.</li>
+ <li>Lancé depuis l'environnement principal de Python ou depuis des environnements virtuels Python séparés.</li>
+</ul>
+
+<p>Chacune de ces options requiert une configuration et une installation légèrement différente. Les sous-sections ci-dessous vous expliquent différents choix. Dans le reste de l'article, nous vous montrerons comment installer Django sur un nombre restreint de systèmes d'exploitation, et nous supposerons que cette installation aura été suivie pour tout le reste du module.</p>
+
+<div class="note">
+<p><strong>Note</strong>: D'autres options d'installation possibles sont traitées dans la documentation officielle de Django. Les liens vers la <a href="https://wiki.developer.mozilla.org/en-US/docs/Learn/Server-side/Django/development_environment$translate?tolocale=fr#">documentation appropriée peuvent-être trouvés ci-dessous</a>.</p>
+</div>
+
+<h4 id="Quels_systèmes_dexploitation_sont_supportés">Quels systèmes d'exploitation sont supportés ?</h4>
+
+<p>Les applications web Django peuvent tourner sous presque n'importe quelle machine pouvant faire fonctionner le langage de programmation Python 3 : Windows, macOS, Linux/Unix, Solaris, pour ne nommer que ceux-là. Quasiment n'importe quel ordinateur devrait avoir les performances nécessaires pour faire fonctionner Django lors de la phase de développement.</p>
+
+<p>Dans cet article, nous vous donnons les instructions pour Windows, macOS et Linux/Unix.</p>
+
+<h4 id="Quelle_version_de_Python_doit-être_utilisée">Quelle version de Python doit-être utilisée ?</h4>
+
+<p>Nous vous recommandons d'utiliser la version la plus récente disponible — au moment de l'écriture de cet article, nous en sommes à la version Python 3.7.2.</p>
+
+<p>Si besoin, les versions de Python 3.5 et ultérieures peuvent être utilisées (le support pour Python 3.5 sera abandonné lors de la sortie des prochaines versions).</p>
+
+<div class="note">
+<p><strong>Note</strong>: Python 2.7 ne peut pas être utilisé avec Django 2.1 (la série Django 1.11.x est la dernière à supporter Python 2.7).</p>
+</div>
+
+<h4 id="Où_peut-on_télécharger_Django">Où peut-on télécharger Django ?</h4>
+
+<p>Il y a trois façons de télécharger Django :</p>
+
+<ul>
+ <li>Le Repository de Packages Python (PyPI), en utilisant l'outil pip. C'est la meilleure façon d'obtenir la dernière version stable de Django.</li>
+ <li>En utilisant la version du gestionnaire de packages de votre ordinateur. Les distributions de Django empaquetées avec les systèmes d'exploitation offrent un mécanisme d'installation plus familier. Veuillez toutefois noter que la version du package peut être datée, et ne pourra alors être installée que dans l'environnement système de Python (ce que vous pourriez ne pas souhaiter).</li>
+ <li>Installation depuis la source : Vous pouvez télécharger et installer la toute dernière version de Django depuis la source. Ce n'est pas recommandé pour les débutants, mais c'est une étape nécessaire si vous souhaitez contribuer à Django lui-même.</li>
+</ul>
+
+<p>Cet article explique comment installer Django depuis PyPI afin d'obtenir la version stable la plus récente.</p>
+
+<h4 id="Quelle_base_de_données">Quelle base de données ?</h4>
+
+<p>Django supporte quatre bases de données principales (PostgreSQL, MySQL, Oracle et SQLite), et des librairies fournies par la communauté offrent différents niveaux de support pour d'autre bases de données SQL et NoSQL populaires. Nous vous recommandons de choisir la même base de données pour la production et le développement (bien que Django puisse abstraire plusieurs différences entre les bases de données en utilisant son Mapper Relationnel-Objet (ORM), il reste tout de même certains <a href="https://docs.djangoproject.com/en/2.1/ref/databases/">problèmes potentiels</a> qu'il vaut mieux éviter).</p>
+
+<p>Dans cet article (et quasiment tout le module), nous utiliserons la base <em>SQLite</em>, qui sauvegarde ses données dans des fichiers. SQLite a été conçu pour être utilisé comme une base de données légère, mais elle ne peut pas supporter un haut niveau de compétition. Elle est cependant un excellent choix pour des applications qui sont prioritairement en lecture seule.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Django est configuré pour utiliser SQLite par défaut lorsque vous démarrez le projet de votre site web en utilisant les outils standards (<em>django-admin</em>). C'est un très bon choix lorsque vous débutez car elle ne requiert aucune configuration ou installation particulière.</p>
+</div>
+
+<h4 id="Installation_globale_ou_dans_un_environnement_virtuel_Python">Installation globale ou dans un environnement virtuel Python ?</h4>
+
+<p>Lorsque vous installez Python3, vous obtenez un environnement global unique partagé par tout le code Python3. Bien que vous puissiez installer n'importe quel package Python souhaité dans cet environnement, vous ne pouvez disposer que d'une seule version d'un package donné à la fois.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Les applications installées dans l'environnement global peuvent potentiellement entrer en conflit avec les autres (i.e. si elles dépendent de versions différentes d'un même package).</p>
+</div>
+
+<p>Si vous installez Django dans l'environnement par défaut/global, vous ne pourrez alors cibler qu'une seule version de Django sur votre machine. Cela peut devenir un problème si vous souhaitez créer de nouveaux sites web (utilisant la dernière version de Django) tout en maintenant d'autres sites web dépendant de versions antérieures.</p>
+
+<p>Ainsi, un développeur Python/Django confirmé lance généralement ses applications Python dans des <em>environnements virtuels Python</em> indépendants. Cela permet d'avoir plusieurs environnements Django sur un seul et même ordinateur. L'équipe de développement de Django elle-même recommande d'utiliser des environnements virtuels Python.</p>
+
+<p>Ce module suppose que vous avez installé Django dans un environnement virtuel, et nous vous montrons comment le faire ci-dessous.</p>
+
+<h2 id="Installer_Python_3">Installer Python 3</h2>
+
+<p>Si vous souhaitez utiliser Django, vous devrez installer Python sur votre système d'exploitation. Si vous utilisez <em>Python 3</em>, vous aurez alors aussi besoin de l'outil <a href="https://pypi.python.org/pypi">Python Package Index</a> — <em>pip3</em> — qui est utilisé pour gérer (installer, mettre à jour, supprimer) les packages/librairies Python qui seront utilisés par Django et vos autres applications Python.</p>
+
+<p>Cette section décrit brièvement comment vérifier quelle version de Python sont disponibles, et comment installer de nouvelles versions si nécessaire, sur Ubuntu Linux 18.04, macOS et Windows 10.</p>
+
+<div class="note">
+<p><strong>Note</strong>: En fonction de votre plateforme, vous aurez probablement aussi besoin d'installer Python/pip depuis le gestionnaire de packages de votre système d'exploitation, ou via d'autre moyens. Pour la plupart des plateformes, vous pouvez télécharger les fichiers d'installation requis depuis <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a> et les installer en utilisant la méthode appropriée à votre plateforme.</p>
+</div>
+
+<h3 id="Ubuntu_18.04">Ubuntu 18.04</h3>
+
+<p>Ubuntu Linux 18.04 LTS inclut par défaut Python 3.6.6. Vous pouvez vous en assurer en exécutant les commandes suivantes depuis le terminal bash :</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">python3 -V
+ Python 3.6.6</span></pre>
+
+<p>Toutefois, l'outil d'Index des Packages Python dont vous aurez besoin pour installer des packages avec Python 3 (y compris Django) n'est <strong>pas </strong>disponible par défaut. Vous pouvez installer pip3 avec le terminal bash avec :</p>
+
+<pre class="brush: bash">sudo apt install python3-pip
+</pre>
+
+<h3 id="macOS">macOS</h3>
+
+<p>macOS "El Capitan"et les versions plus récentes n'incluent pas Python 3. Vous pouvez vous en assurer en exécutant les commandes suivantes dans votre terminal bash :</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">python3 -V
+ </span>-bash: python3: command not found</pre>
+
+<p>Vous pouvez facilement installer Python 3 (ainsi que l'outil <em>pip3</em>) sur <a href="https://www.python.org/">python.org</a>:</p>
+
+<ol>
+ <li>Téléchargez l'installeur requis :
+ <ol>
+ <li>Allez sur <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></li>
+ <li>Sélectionnez le bouton <strong>Download Python 3.7.2</strong> (le numéro de version mineure peut varier).</li>
+ </ol>
+ </li>
+ <li>Localisez le fichier en utilisant le <em>Finder</em>, puis double-cliquez le fichier package. Suivez les consignes d'installation.</li>
+</ol>
+
+<p>Vous pouvez désormais confirmer la bonne installation en vérifiant votre version de Python 3 comme indiqué ci-dessous :</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">python3 -V
+ Python 3.7.2</span>
+</pre>
+
+<p>Vous pouvez aussi vérifier que pip3 est correctement installé en listant les packages disponibles :</p>
+
+<pre class="brush: bash">pip3 list</pre>
+
+<h3 id="Windows_10">Windows 10</h3>
+
+<p>Windows n'inclut pas Python par défaut, mais vous pouvez facilement l'installer (ainsi que l'outil <em>pip3</em>) sur<a href="https://www.python.org/"> python.org</a>:</p>
+
+<ol>
+ <li>Téléchargez l'installeur requis :
+ <ol>
+ <li>Allez sur <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></li>
+ <li>Sélectionnez le bouton <strong>Download Python 3.7.2</strong> (le numéro de version mineure peut varier).</li>
+ </ol>
+ </li>
+ <li>Installez Python en double-cliquant sur le fichier télécharger puis en suivant les consignes d'installation</li>
+ <li>Assurez-vous d'avoir coché la case intitulée "Ajouter Python au PATH".</li>
+</ol>
+
+<p>Vous pouvez ensuite vérifier que Python s'est correctement installé en tapant le texte suivant dans votre invite de commande :</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">py -3 -V
+ Python 3.7.2</span>
+</pre>
+
+<p>L'installeur Windows inclut <em>pip3</em> (le gestionnaire de packages Python) par défaut. Vous pouvez lister les packages installés de la manière suivante :</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">pip3 list</span>
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: L'installeur devrait configurer tout ce dont vous avez besoin pour que les commandes ci-dessus fonctionnent. Toutefois, si vous obtenez un message vous indiquant que Python ne peut pas être trouvé (Python cannot be found), il est possible que vous ayez oublié de l'ajouter à votre PATH système. Vous pouvez faire cela en réexécutant l'installeur, sélectionnez "Modifier", puis cochez la case intitulée "Ajouter Python aux variables d'environnement" sur le deuxième page.</p>
+</div>
+
+<h2 id="Utiliser_Django_dans_un_environnement_virtuel_Python">Utiliser Django dans un environnement virtuel Python</h2>
+
+<p>Les librairies que nous utiliserons pour créer nos environnements virtuels seront <a href="https://virtualenvwrapper.readthedocs.io/en/latest/index.html">virtualenvwrapper</a> (Linux et macOS) et <a href="https://pypi.python.org/pypi/virtualenvwrapper-win">virtualenvwrapper-win</a> (Windows), , qui à leur tour utilisent l'outil <a href="https://developer.mozilla.org/en-US/docs/Python/Virtualenv">virtualenv</a>. Les outils d'habillage permettent de créer une interface consistante pour gérer les interfaces sur toutes les plateformes.</p>
+
+<h3 id="Installer_lutilitaire_denvironnement_virtuel">Installer l'utilitaire d'environnement virtuel</h3>
+
+<h4 id="Mise_en_place_de_lenvironnement_virtuel_sur_Ubuntu">Mise en place de l'environnement virtuel sur Ubuntu</h4>
+
+<p>Après avoir installé Python et pip vous pouvez installer <em>virtualenvwrapper</em> (qui inclut <em>virtualenv</em>). Le guide d'installation officiel peut être trouvé <a href="http://virtualenvwrapper.readthedocs.io/en/latest/install.html">ici</a>, ou bien vous pouvez suivre les instructions ci-dessous.</p>
+
+<p>Installer l'outil en utilisant <em>pip3</em>:</p>
+
+<pre class="brush: bash"><code>sudo pip3 install virtualenvwrapper</code></pre>
+
+<p>Ajoutez ensuite les lignes suivantes à la fin de votre fichier de configuration shell (le fichier caché <strong>.bashrc</strong> dans votre répertoire home). Elles indiquent les endroits où vos environnements virtuels seront installés, l'emplacement de vos projets de développement, et l'emplacement du script installé avec ce package :</p>
+
+<pre class="brush: bash"><code>export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export VIRTUALENVWRAPPER_VIRTUALENV_ARGS=' -p /usr/bin/python3 '
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh</code>
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Les variables <code>VIRTUALENVWRAPPER_PYTHON</code> et <code>VIRTUALENVWRAPPER_VIRTUALENV_ARGS </code>pointent vers l'emplacement d'installation par défaut de Python3, et <code>source /usr/local/bin/virtualenvwrapper.sh</code> pointe vers l'emplacement par défaut du script <code>virtualenvwrapper.sh</code>. Si le <em>virtualenv</em> ne fonctionne pas quand vous le testez, vérifiez que Python et le script sont bien aux emplacements attendus (puis modifiez le fichier de configuration en conséquence).<br>
+ <br>
+ Vous pourrez trouver les bons emplacements de votre système en utilisant les commandes <code>which virtualenvwrapper.sh</code> et <code>which python3</code>.</p>
+</div>
+
+<p>Puis relancez le fichier de configuration en exécutant la commande suivante dans votre terminal :</p>
+
+<pre class="brush: bash"><code>source ~/.bashrc</code></pre>
+
+<p>Vous devriez alors voir apparaître plusieurs lignes de script semblables à celles ci-dessous :</p>
+
+<pre class="brush: bash"><code>virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/premkproject
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postmkproject
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/preactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/get_env_details</code>
+</pre>
+
+<p>Vous pouvez maintenant créer un nouvel environnement virtuel avec la commande <code>mkvirtualenv</code>.</p>
+
+<h4 id="Mise_en_place_de_lenvironnement_virtuel_sur_macOS">Mise en place de l'environnement virtuel sur macOS</h4>
+
+<p>L'installation de <em>virtualenvwrapper</em> on sur macOS est quasiment identique à celle sur Ubuntu (une fois de plus, vous pouvez suivre les instructions du <a href="http://virtualenvwrapper.readthedocs.io/en/latest/install.html">guide officiel d'installation</a> ou suivre les indications ci-dessous).</p>
+
+<p>Installez <em>virtualenvwrapper</em> (ainsi que <em>virtualenv</em>) en utilisant <em>pip</em> comme indiqué ci-dessous.</p>
+
+<pre class="brush: bash"><code>sudo pip3 install virtualenvwrapper</code></pre>
+
+<p>Puis ajoutez les lignes suivantes à la fin de votre fichier de configuration shell.</p>
+
+<pre class="brush: bash"><code>export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh</code></pre>
+
+<div class="note">
+<p><strong>Note</strong>: La variable <code>VIRTUALENVWRAPPER_PYTHON</code> pointe vers l'emplacement d'installation par défaut de Python3, et <code>source /usr/local/bin/virtualenvwrapper.sh</code> pointe vers l'emplacement par défaut du script <code>virtualenvwrapper.sh</code>. Si le <em>virtualenv</em> ne fonctionne pas quand vous le testez, vérifiez que Python et le script sont bien aux emplacements attendus (puis modifiez le fichier de configuration en conséquence).</p>
+
+<p>Par exemple, une installation test sur macOS a résulté en l'ajout des lignes suivantes dans le fichier startup :</p>
+
+<pre class="brush: bash">export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/Library/Frameworks/Python.framework/Versions/3.7/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /Library/Frameworks/Python.framework/Versions/3.7/bin/virtualenvwrapper.sh</pre>
+
+<p>Vous pourrez trouver les bons emplacements de votre système en utilisant les commandes <code>which virtualenvwrapper.sh</code> et <code>which python3</code>.</p>
+</div>
+
+<p>Ce sont les mêmes lignes que pour Ubuntu, mais le nom du fichier de configuration caché du répertoire home est différent : <strong>.bash_profile</strong> dans votre répertoire home.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Si vous n'arrivez pas à trouver le fichier <strong>.bash_profile</strong> depuis le finder, vous pouvez aussi l'ouvrir depuis le terminal en utilisant nano.</p>
+
+<p>La commande sera la suivante :</p>
+
+<pre><code>cd ~ # Naviguer vers le répertoire home
+ls -la # Listez le contenu du répertoire. Vous devriez voir .bash_profile.
+nano .bash_profile # Ouvrez le fichier avec l'éditeur de texte nano, depuis le terminal.
+# Allez à la fin du fichier, puis copiez-collez les lignes ci-dessus.
+# Utilisez Ctrl+X pour quitter nano, sélectionnez Y pour sauvegarder le fichier.</code>
+</pre>
+</div>
+
+<p>Puis relancez le fichier de configuration en appelant la ligne suivante depuis le terminal :</p>
+
+<pre class="brush: bash"><code>source ~/.bash_profile</code></pre>
+
+<p>Vous devriez alors voir apparaître plusieurs lignes de script (les mêmes scripts que ceux présents dans l'installation Ubuntu). Vous devriez maintenant pouvoir créer un nouvel environnement virtuel avec la commande <code>mkvirtualenv</code>.</p>
+
+<h4 id="Mise_en_place_de_lenvironnement_virtuel_sur_Windows_10">Mise en place de l'environnement virtuel sur Windows 10</h4>
+
+<p>Installer <a href="https://pypi.python.org/pypi/virtualenvwrapper-win">virtualenvwrapper-win</a> est encore plus simple qu'installer <em>virtualenvwrapper</em> , parce que vous n'avez pas besoin de configurer là où l'outil enregistre les informations de l'environnement virtuel (il y a des valeurs par défaut). Tout ce que vous avez besoin de faire est de lancer la commande suivante depuis l'invite de commande :</p>
+
+<pre><code>pip3 install virtualenvwrapper-win</code></pre>
+
+<p>Vous pouvez désormais créer un nouvel environnement virtuel avec la commande <code>mkvirtualenv</code></p>
+
+<h3 id="Créer_un_environnement_virtuel">Créer un environnement virtuel</h3>
+
+<p>Maintenant que vous avez installé <em>virtualenvwrapper</em> ou <em>virtualenvwrapper-win</em> , travailler avec des environnements virtuels sera une tâche très similaire entre chaque plateforme.</p>
+
+<p>Vous pouvez désormais créer un nouvel environnement virtuel avec la commande <code>mkvirtualenv</code>. Lors de son exécution, vous pourrez voir l'environnement être configuré (ce que vous verrez changera légèrement en fonction de votre plateforme). Lorsque l'exécution de la commande sera terminée, votre environnement virtuel sera actif — vous pouvez voir au début de la ligne de commande le nom de votre environnement entre parenthèses (nous vous montrons le résultat pour Ubuntu ci-dessous, mais la dernière ligne est similaire sur Windows/macOS).</p>
+
+<pre><code>$ mkvirtualenv my_django_environment
+
+Running virtualenv with interpreter /usr/bin/python3
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/t_env7/bin/get_env_details
+(my_django_environment) ubuntu@ubuntu:~$</code>
+</pre>
+
+<p>Maintenant que vous êtes dans votre environnement virtuel vous pouvez installer Django et commencer à développer.</p>
+
+<div class="note">
+<p><strong>Note</strong>: A partir de ce point dans l'article (et donc dans le module), vous pourrez considérer que toutes les commandes seront exécutées dans un environnement virtuel Python comme celui que nous avons mis en place plus haut.</p>
+</div>
+
+<h3 id="Utiliser_un_environnement_virtuel">Utiliser un environnement virtuel</h3>
+
+<p>Il y a quelques commandes que vous devriez connaître (il y en a davantage dans la documentation de l'outil, mais celles-ci sont celles que vous utiliserez le plus souvent) :</p>
+
+<ul>
+ <li><code>deactivate</code> — Permet de sortir de l'environnement virtuel Python actuel</li>
+ <li><code>workon</code> — Lister tous les environnements virtuels disponibles</li>
+ <li><code>workon name_of_environment</code> — Activer l'environnement virtuel spécifié</li>
+ <li><code>rmvirtualenv name_of_environment</code> — Supprimer l'environnement virtuel spécifié</li>
+</ul>
+
+<h2 id="Installer_Django">Installer Django</h2>
+
+<p>Une fois que vous avez créé votre environnement virtuel, et que vous avez utilisé <code>workon</code> pour y entrer, vous pouvez utiliser <em>pip3 </em>pour installer Django. </p>
+
+<pre class="brush: bash">pip3 install django
+</pre>
+
+<p>Vous pouvez tester l'installation de Django en exécutant la commande suivante (celle-ci ne fait que tester le fait que Python puisse trouver le module Django) :</p>
+
+<pre class="brush: bash"># Linux/macOS
+python3 -m django --version
+ 2.1.5
+
+# Windows
+py -3 -m django --version
+ 2.1.5
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Si la commande Windows ci-dessus n'indique aucun module Django présent, essayez :</p>
+
+<pre class="brush: bash">py -m django --version</pre>
+
+<p>Dans Windows, les scripts <em>Python 3</em> sont exécutés en préfixant la commande avec <code>py -3</code>, bien que ceci puisse varier suivant votre installation. Essayer en enlevant le modificateur <code>-3 </code>si vous rencontrez un problème avec la commande. Dans Linux/macOS, la commande est <code>python3.</code></p>
+</div>
+
+<div class="warning">
+<p><strong>Important</strong>: Le reste de ce <strong>module </strong>utilise les commandes <em>Linux</em> pour invoquer Python 3 (<code>python3</code>) . . Si vous travaillez sous <em>Windows </em>, remplacez simplement ce préfixe avec : <code>py -3</code></p>
+</div>
+
+<h2 id="Tester_votre_installation">Tester votre installation</h2>
+
+<p>Les tests ci-dessus fonctionnent, mais ne font rien de très divertissant. Un test plus intéressant consiste à créer un projet squelette et de voir si il fonctionne. Pour ce faire, naviguez tout d'abord dans votre invite/terminal de commande à l'endroit où vous désirez stocker vos applications Django. Créez un dossier pour votre site test et placez-vous dedans.</p>
+
+<pre class="brush: bash">mkdir django_test
+cd django_test
+</pre>
+
+<p>Vous pouvez ensuite créer un nouveau site squelette intitulé "<em>mytestsite</em>" utilisant l'outil <strong>django-admin</strong> omme montré. Après avoir créé le site, vous pouvez naviguer dans le dossier où vous trouverez le script principal pour gérer vos projets, intitulé <strong>manage.py</strong>.</p>
+
+<pre class="brush: bash">django-admin startproject mytestsite
+cd mytestsite</pre>
+
+<p>Nous pouvons lancer le <em>serveur web de développement</em> depuis ce dossier en utilisant <strong>manage.py</strong> et la commande <code>runserver</code> command, comme indiqué ci-dessous.</p>
+
+<pre class="brush: bash">$ python3 manage.py runserver
+Performing system checks...
+
+System check identified no issues (0 silenced).
+
+You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
+Run 'python manage.py migrate' to apply them.
+
+December 16, 2018 - 07:06:30
+Django version 2.1.5, using settings 'mytestsite.settings'
+Starting development server at http://127.0.0.1:8000/
+Quit the server with CONTROL-C.
+</pre>
+
+<div class="note">
+<p><strong>Note </strong>: La commande ci-dessus montre le résultat de l'exécution sur Linux/macOS. Vous pouvez ignorer les warnings à propos des "15 unapplied migration(s)" à partir de là !</p>
+</div>
+
+<p>Maintenant que votre serveur est lancé, vous pouvez voir le site en naviguant vers l'URL suivante depuis votre navigateur local : <code>http://127.0.0.1:8000/</code>. Vous devriez voir un site ressemblant à celui-ci :<br>
+ <img alt="Django Skeleton App Homepage - Django 2.0" src="https://mdn.mozillademos.org/files/16288/Django_Skeleton_Website_Homepage_2_1.png" style="height: 714px; width: 806px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Vous avez maintenant un environnement de développement Django fonctionnel et opérationnel sur votre ordinateur.</p>
+
+<p>Dans la section test vous avez aussi vu comment créer un nouveau site web Django en utilisant <code>django-admin startproject</code>, et comment aller dessus depuis votre navigateur en utilisant le serveur de développement web (<code>python3 manage.py runserver</code>). Dans le prochain article nous détaillerons ce processus, et créant un application web simple mais complète.</p>
+
+<h2 id="See_also">See also</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.1/intro/install/">Quick Install Guide</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/topics/install/">How to install Django — Complete guide</a> (Django docs) - includes information on how to remove Django</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/howto/windows/">How to install Django on Windows</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}</p>
+
+<h2 id="In_this_module">In this module</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/fr/learn/server-side/django/forms/index.html b/files/fr/learn/server-side/django/forms/index.html
new file mode 100644
index 0000000000..0c877c8946
--- /dev/null
+++ b/files/fr/learn/server-side/django/forms/index.html
@@ -0,0 +1,651 @@
+---
+title: 'Django Tutorial Part 9: Working with forms'
+slug: Learn/Server-side/Django/Forms
+tags:
+ - Beginner
+ - CodingScripting
+ - DjangoForms
+ - Forms
+ - HTML forms
+ - Learn
+ - Tutorial
+ - django
+ - server side
+translation_of: Learn/Server-side/Django/Forms
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">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="https://developer.mozilla.org/en-US/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>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis:</th>
+ <td>Avoir terminé les formations précédentes, y compris <a href="/en-US/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>
+
+<p>Un <a href="/en-US/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>
+
+<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="/en-US/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>
+
+<p><img alt="Admin Site - Book Add" src="https://mdn.mozillademos.org/files/13979/admin_book_add.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<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>
+
+<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>
+
+<h2 id="Formulaires_HTML">Formulaires HTML</h2>
+
+<p>D'abord, un premier aperçu des formulaires HTML (<a href="/en-US/docs/Learn/HTML/Forms" style='background-color: rgb(255, 255, 255); font-family: "Open Sans", arial, x-locale-body, sans-serif;'>HTML Forms</a>). <span style='background-color: #ffffff; color: #333333; font-family: "Open Sans",arial,x-locale-body,sans-serif;'>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 :</span></p>
+
+<p><img alt="Simple name field example in HTML form" src="https://mdn.mozillademos.org/files/14117/form_example_name_field.png" style="border-style: solid; border-width: 1px; display: block; height: 44px; margin: 0px auto; width: 399px;"></p>
+
+<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>
+
+<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>
+
+<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>
+
+<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<font face="consolas, Liberation Mono, courier, monospace"> 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>):</font></p>
+
+<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>
+
+<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>
+
+<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>
+
+<h2 id="Les_étapes_de_gestion_dun_formulaire_avec_Django">Les étapes de gestion d'un formulaire avec Django</h2>
+
+<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>
+
+<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>
+
+<p><img alt="Updated form handling process doc." src="https://mdn.mozillademos.org/files/14205/Form%20Handling%20-%20Standard.png" style="display: block; height: 569px; margin: 0px auto; width: 800px;"></p>
+
+<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>
+
+<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>
+
+<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>
+
+<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>
+
+<h2 id="Formulaire_de_renouvellement_de_livre_par_lutilisation_de_vue_Form">Formulaire de renouvellement de livre par l'utilisation de vue Form</h2>
+
+<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>
+
+<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>
+
+<h3 id="Formulaire">Formulaire</h3>
+
+<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>
+
+<h4 id="Déclarer_un_formulaire">Déclarer un formulaire</h4>
+
+<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>
+
+<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>
+
+<pre class="brush: 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>
+
+<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>
+
+<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>
+
+<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>
+
+<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>
+
+<h4 id="Validation">Validation</h4>
+
+<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>
+
+<p>Mettez à jour votre fichier forms.py, de telle sorte qu'il ressemble à cela :</p>
+
+<pre class="brush: python">from django import forms
+
+<strong>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):
+        data = self.cleaned_data['renewal_date']
+
+        #Check date is not in past.
+        if data &lt; 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):
+            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+        # Remember to always return the cleaned data.
+        return data</strong></pre>
+
+<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>
+
+<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>
+
+<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>
+
+<p>C'est tout ce dont nous avons besoin pour notre formulaire dans cet exemple.</p>
+
+<h3 id="Configuration_dURL">Configuration d'URL</h3>
+
+<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>
+
+<pre class="brush: python">urlpatterns += [
+ url(r'^book/(?P&lt;pk&gt;[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'),
+]</pre>
+
+<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>
+
+<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>
+
+<h3 id="Vue">Vue</h3>
+
+<p>Comme nous l'avons expliqué ci-dessus dans <a href="https://developer.mozilla.org/en-US/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>
+
+<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>
+
+<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>
+
+<pre class="brush: python">import datetime
+
+from django.shortcuts import get_object_or_404
+from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+
+from .forms import RenewBookForm
+
+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>
+
+        # 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>
+            # 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') )
+
+    # If this is a GET (or any other method) create the default form.
+<strong>    else:</strong>
+        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>
+
+<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>
+
+<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>
+
+<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>
+
+<pre class="brush: 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>
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<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>
+
+<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>
+
+<pre class="brush: 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>
+
+        # 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') )
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<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>
+
+<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>
+
+<div class="warning">
+<p><strong>Important </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>
+
+<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="https://developer.mozilla.org/en-US/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>
+
+<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>
+
+<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>
+
+<pre><strong>from django.contrib.auth.decorators import permission_required</strong>
+
+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
+
+<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)
+
+    # 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)
+
+        # 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') )
+
+    # 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>
+
+<h3 id="Le_template">Le template</h3>
+
+<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>
+
+<pre class="brush: 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;
+
+<strong> &lt;form action="" method="post"&gt;
+ {% csrf_token %}
+ &lt;table&gt;
+ \{{ form }}
+  &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+ &lt;/form&gt;</strong>
+
+{% endblock %}</pre>
+
+<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>
+
+<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="https://developer.mozilla.org/en-US/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>
+
+<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>
+
+<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>
+
+<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>
+
+<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>
+
+<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>
+
+<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>
+
+<h4 id="Autres_façons_dutiliser_la_variable_de_template_du_formulaire">Autres façons d'utiliser la variable de template du formulaire</h4>
+
+<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>
+
+<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>
+
+<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>
+
+<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>
+
+<h3 id="Tester_la_page">Tester la page</h3>
+
+<p>Si vous avez accepté le "challenge" dans <a href="https://developer.mozilla.org/en-US/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>
+
+<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>
+
+<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>
+
+<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>
+
+<h3 id="À_quoi_cela_ressemble-t-il">À quoi cela ressemble-t-il ?</h3>
+
+<p>Si tout a bien marché, le formulaire par défaut ressemblera à ceci :</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14209/forms_example_renew_default.png" style="border-style: solid; border-width: 1px; display: block; height: 292px; margin: 0px auto; width: 680px;"></p>
+
+<p>Le formulaire avec valeur erronée ressemblera à ceci :</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14211/forms_example_renew_invalid.png" style="border-style: solid; border-width: 1px; display: block; height: 290px; margin: 0px auto; width: 658px;"></p>
+
+<p>La liste de tous les livres avec les liens vers le renouvellement ressemblera à ceci :</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14207/forms_example_renew_allbooks.png" style="border-style: solid; border-width: 1px; display: block; height: 256px; margin: 0px auto; width: 613px;"></p>
+
+<h2 id="ModelForms">ModelForms</h2>
+
+<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>
+
+<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>
+
+<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>
+
+<pre class="brush: python">from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+<strong> class Meta:
+ model = BookInstance
+ fields = ['due_back',]</strong>
+</pre>
+
+<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>
+
+<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>
+
+<pre class="brush: 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>
+
+<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>
+
+<pre class="brush: python">from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+<strong>    def clean_due_back(self):
+       data = self.cleaned_data['due_back']
+
+  #Check date is not in past.
+       if data &lt; 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):
+           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>
+
+<h2 id="Vues_génériques_dédition">Vues génériques d'édition</h2>
+
+<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>
+
+<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>
+
+<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>
+
+<h3 id="Vues">Vues</h3>
+
+<p>Ouvrez le fichier vue (<strong>locallibrary/catalog/views.py</strong>) et ajoutez le bloc de code suivant à la fin :</p>
+
+<pre class="brush: python">from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from django.urls import reverse_lazy
+from .models import Author
+
+class AuthorCreate(CreateView):
+ model = Author
+ fields = '__all__'
+ initial={'date_of_death':'12/10/2016',}
+
+class AuthorUpdate(UpdateView):
+ model = Author
+ fields = ['first_name','last_name','date_of_birth','date_of_death']
+
+class AuthorDelete(DeleteView):
+ model = Author
+ success_url = reverse_lazy('authors')</pre>
+
+<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>
+
+<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>
+
+<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>
+
+<h3 id="Templates">Templates</h3>
+
+<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>
+
+<p>Créez le fichier de template <strong>locallibrary/catalog/templates/catalog/author_form.html</strong>, et copiez-y le texte suivant.</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+
+&lt;form action="" method="post"&gt;
+ {% csrf_token %}
+ &lt;table&gt;
+ \{{ form.as_table }}
+ &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+
+&lt;/form&gt;
+{% endblock %}</pre>
+
+<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>
+
+<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>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+
+&lt;h1&gt;Delete Author&lt;/h1&gt;
+
+&lt;p&gt;Are you sure you want to delete the author: \{{ author }}?&lt;/p&gt;
+
+&lt;form action="" method="POST"&gt;
+ {% csrf_token %}
+ &lt;input type="submit" action="" value="Yes, delete." /&gt;
+&lt;/form&gt;
+
+{% endblock %}
+</pre>
+
+<h3 id="Configurations_dURL">Configurations d'URL</h3>
+
+<p>Ouvrez votre fichier de configuration d'URL (<strong>locallibrary/catalog/urls.py</strong>) et ajoutez-y à la fin la configuration suivante :</p>
+
+<pre class="brush: 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>
+
+<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>
+
+<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>
+
+<h3 id="Test_de_la_page">Test de la page</h3>
+
+<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>
+
+<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>
+
+<p><img alt="Form Example: Create Author" src="https://mdn.mozillademos.org/files/14223/forms_example_create_author.png" style="border-style: solid; border-width: 1px; display: block; height: 184px; margin: 0px auto; width: 645px;"></p>
+
+<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>
+
+<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>
+
+<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>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14221/forms_example_delete_author.png" style="border-style: solid; border-width: 1px; display: block; height: 194px; margin: 0px auto; width: 561px;"></p>
+
+<h2 id="Mettez-vous_au_défi">Mettez-vous au défi</h2>
+
+<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>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14225/forms_example_create_book.png" style="border-style: solid; border-width: 1px; display: block; height: 521px; margin: 0px auto; width: 595px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="Résumé">Résumé</h2>
+
+<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>
+
+<p>Il y a bien d'autres choses qui peuvent être faites avec les formulaires (regardez notre liste <a href="https://developer.mozilla.org/en-US/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>
+
+<h2 id="See_also">See also</h2>
+
+<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>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</p>
diff --git a/files/fr/learn/server-side/django/home_page/index.html b/files/fr/learn/server-side/django/home_page/index.html
new file mode 100644
index 0000000000..91f6ef16bf
--- /dev/null
+++ b/files/fr/learn/server-side/django/home_page/index.html
@@ -0,0 +1,429 @@
+---
+title: 'Django didactique Section 5: Créer la page d''accueil'
+slug: Learn/Server-side/Django/Home_page
+tags:
+ - Article
+ - Cadriciel
+ - Code
+ - Didactique
+ - Django (Vues)
+ - Django (gabarits)
+ - Débutant
+ - Programmation
+ - Python
+ - django
+translation_of: Learn/Server-side/Django/Home_page
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Le travail préparatoire pour nous permettre de créer une page d'accueil pour le site web de <a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">la bibliothèque locale</a> est réalisé. La page d'accueil montera le nombre d'enregistrements pour chacun des objets décrits dans la base et les liens à l'aide d'une barre latérale de navigation. Dans la progression de l'article, nous apprendrons à gérer les vues et à présenter les données à l'aide de gabarits.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pré-requis:</th>
+ <td>L'<a href="/fr/docs/Learn/Server-side/Django/Introduction">introduction</a> à cette série didactique et les sections précédentes y compris celle sur <a href="/fr/docs/Learn/Server-side/Django/Admin_site">le site d'administration</a> du site web.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objective:</th>
+ <td>Apprendre à construire un routage d'URL et les pages de publication des vues (où les données ne sont pas encodées dans l'url). Obtenir et publier des données via les objets du modèle de données et les publier à l'aide de gabarits.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Survol">Survol</h2>
+
+<p>Dans les sections précédentes, nous avons défini <a href="/fr/docs/Learn/Server-side/Django/Models">le modèle de données et les objets Dajngo à manipuler</a>, puis nous avons commencé à peupler <a href="/fr/docs/Learn/Server-side/Django/Admin_site">des enregistrements à l'aide du site d'administration</a>. Désormais, nous allons œuvrer à la présentation des données et développer le code nécessaire à l'information des utilisateurs. La première étape essentielle est de déterminer les informations que nous souhaitons publier dans nos différentes pages et, par conséquent, identifier les URL qui pourvoiront à la publication de ces informations. Nous serons alors en capacité de construire les routage d'URL, les vues et gabarits qui répondront aux exigences définies.</p>
+
+<p>Le diagramme ci-dessous est important à comprendre car il est au cœur du fonctionnement du cadriciel Django. Il décrit les flux de données et les composants sollicités pour traiter et répondre à une requête HTTP. Nous avons déjà travaillé le modèle de données (à gauche du diagramme), nous allons désormais nous atteler à :</p>
+
+<ul>
+ <li>détailler le routage des URL pour associer les vues adaptées aux requêtes HTTP que le site devra traiter (y compris avec des informations encodées dans les URL).</li>
+ <li>définir les fonctions de visualisation et créer les pages HTML qui vont permettre de publier les informations à destination des utilisateurs du site.</li>
+ <li>créer les gabarits qui vont permettre de publier les données dans les vues.</li>
+</ul>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13931/basic-django.png" style="display: block; margin: 0px auto;"></p>
+
+<p>Nous aurons à créer 5 pages web pour publier les informations à destination des utilisateurs. Cela fait beaucoup d'éléments à maîtriser dans une seule section d'apprentissage de l'outils. Nous avons donc opté pour ne traiter dans cette section que de la page d'accueil et de traiter les autres pages dans une autre section du didacticiel. Cela permet, en outre, de mieux appréhender les composants comme le routage d'URL ou les vues et d'une manière générale le fonctionnement du modèle de Django.</p>
+
+<h2 id="Identifier_les_URLs_des_ressources">Identifier les URLs des ressources</h2>
+
+<p>Le site web de la bibliothèque locale est essentiellement un site de consultation pour les adhérents de la bibliothèque, nous aurons donc, par conséquent, besoin uniquement de pages pour les vues de détail de chacun des livres (ouvrages, auteur, etc.) de la bibliothèque et d'une page d'accueil.</p>
+
+<p>La liste des URLs dont nous aurons besoin se résume à :</p>
+
+<ul>
+ <li><code>catalog/</code> — Pour la page d'accueil.</li>
+ <li><code>catalog/books/</code> — Pour la liste des livres.</li>
+ <li><code>catalog/authors/</code> — Pour la liste des auteurs.</li>
+ <li><code>catalog/book/<em>&lt;id&gt;</em></code> — Pour disposer du détails de chacun des livres mis en prêt et identifiés par identifiants <code><em>&lt;id&gt;</em></code> unique (le troisième livre enregistré est consultable dans le détail via l'url <code>/catalog/book/3</code>).</li>
+ <li><code>catalog/author/<em>&lt;id&gt;</em></code><em> </em>— De la même manière, le détail de chacun des auteurs enregistrés, identifié de la la même manière par sa clé primaire <em><code>&lt;id&gt;</code></em>.</li>
+</ul>
+
+<p>Bien que les données dépendent du contenu de la base de données, les trois premières URLs retournent les résultats de requêtes sans informations supplémentaires ; c'est le cas de la page d'accueil qui donnera des décomptes de contenus et des pages sur la listes des livres ou des auteurs.</p>
+
+<p>En revanche, les pages concernant le détails d'un livre ou d'un auteur nécessiteront de traiter l'identifiant d'un  objet. Il sera nécessaire d'extraire de la requête HTTP l'information encodé de cet identifiant pour obtenir ensuite le détail depuis la base de données. Pour cela nous utiliserons un seul jeu de vue et de gabarit pour publier les informations sur les livres (et auteurs).</p>
+
+<div class="note">
+<p><strong>Note</strong>: Avec le cadriciel Django, vous pouvez élaborer vos URLs comme bon vous semble — Vous pouvez avoir une approche comme celle présentée ci-dessus, ou de faire un appel de la méthode <code>GET</code> avec un passage de paramètres (<code>/book/?id=6</code>). Cependant, quelque soit l'approche pour laquelle vous opterez, garder en mémoire d'avoir des URLs claires, logique et compréhensibles comme cela est  <a href="https://www.w3.org/Provider/Style/URI">recommandé par le W3C</a>.</p>
+
+<p>La documentation de Django recommande aussi de coder les informations dans le corps des URLs pour avoir une meilleure conception de ces URLs.</p>
+</div>
+
+<p>La suite de cette section s'intéresse à la conception de la page d'accueil.</p>
+
+<h2 id="Création_de_la_page_d'accueil">Création de la page d'accueil</h2>
+
+<p>La toute première page à créer est la page d'accueil (<code>catalog/</code>). Cette page d'index est globalement une page statique contenant le décompte des différents enregistrements de la base de données. Pour faire cela, il sera nécessaire de créer un routage d'URL, une vue et un gabarit. </p>
+
+<div class="note">
+<p><strong>Note</strong>: Cette section est essentielle, et cela vaut vraiment la peine d'être attentif aux concepts déployés dans celle-ci. La plupart des éléments aborder ici seront ré-utilisés par la suite.</p>
+</div>
+
+<h3 id="Routage_d'URL">Routage d'URL</h3>
+
+<p>Quand nous avons créé <a href="/fr/docs/Learn/Server-side/Django/skeleton_website">le squelette du site</a>, nous avons mis à jour les routage des urls dans le fichier <strong>locallibrary/urls.py</strong> afin de nous assurer que toutes les requêtes démarrant par <code>catalog/</code>  seront traités par le configurateur <em>URLConf du module</em> <code>catalog.urls</code> qui traitera la sous-chaîne restante.</p>
+
+<p>L'extrait du code ci-dessous permet d'intégrer dans <strong>locallibrary/urls.py </strong>le configurateur d'url du module <code><em>catalog</em></code> :</p>
+
+<pre>urlpatterns += [
+    path('catalog/', include('catalog.urls')),
+]
+</pre>
+
+<p>Il est désormais nécessaire de créer un configurateur d'URL du module <code>catalog</code> (<em>URLConf </em>du module est nommé <strong>/catalog/urls.py</strong>). Ajoutez le chemin ci-dessous :</p>
+
+<pre class="brush: python">urlpatterns = [
+<strong> path('', views.index, name='index'),</strong>
+]</pre>
+
+<p>La fonction <code>path()</code> sert à définir les éléments suivants :</p>
+
+<ul>
+ <li>Un modèle d'URL qui, dans le cas présent, est une chaîne vide : <code>''</code>. Nous évoquerons ultérieurement les modèles d'URL plus en détail quand nous travaillerons les autres vues.</li>
+ <li>Une fonction du vue, ici <code>views.index</code>, qui sera sollicitée quand le modèle d'URL sera détecté et une fonction Python qui sera appelée pour traiter l'appel d'URL est présent dans le fichier <strong>views.py </strong>du module <code>catalog</code>.</li>
+</ul>
+
+<p>La paramètre <code>name</code> utilisé dans la fonction <code>path()</code> permet aussi de définir un identifiant unique qui sert à lier les pages vers celle-ci au sein de l'application. Vous pouvez alors l'utiliser à l'envers en routant dynamiquement des pages en lien vers cette ressource :</p>
+
+<pre class="brush: html">&lt;a href="<strong>{% url 'index' %}</strong>"&gt;Home&lt;/a&gt;.</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Vus pourriez bien évidemment coder en dur l'accès à la page d'accueil de cette manaière <code>&lt;a href="<strong>/catalog/</strong>"&gt;Home&lt;/a&gt;</code>), mais si nous changions le modèle d'URL, par exemple en <code>/catalog/index</code>, alors le gabarit ne fonctionnerait plus correctement et présenterait un lien mort. L'utilisation des noms et des routage inversés est plus robuste et adapté aux évolutions de l'application.</p>
+</div>
+
+<h3 id="Vue_(View_function-based)">Vue (<em>View function-based</em>)</h3>
+
+<p>Une vue est une fonction qui traite une requête HTTP, extrait des données de la base de données et les restitue dans une page HTML à l'aide d'une réponse HTTPA que le navigateur mettra en forme pour l'utilisateur. La fonction <code>index()</code> suit ce modèle de traitement de l'information : elle extrait les informations sur le nombre de livres, d'ouvrage en rayon ou en prêt et d'auteur enregistrés dans la base de données et à l'aide d'un gabarit les publie.</p>
+
+<p>Editer le fichier <strong>catalog/views.py</strong>.Vous constaterez l'import de la fonction <a href="https://docs.djangoproject.com/en/2.1/topics/http/shortcuts/#django.shortcuts.render">render()</a> qui traite de la génération HTML en utilisat des garabits et des données : </p>
+
+<pre class="brush: python">from django.shortcuts import render
+
+# Create your views here.
+</pre>
+
+<p>Copiez les lignes ci-dessous dans le fichier :</p>
+
+<pre class="brush: python">from catalog.models import Book, Author, BookInstance, Genre
+
+def index(request):
+ """View function for home page of site."""
+
+ # Generate counts of some of the main objects
+ num_books = Book.objects.all().count()
+ num_instances = BookInstance.objects.all().count()
+
+ # Available books (status = 'a')
+ num_instances_available = BookInstance.objects.filter(status__exact='a').count()
+
+ # The 'all()' is implied by default.
+ num_authors = Author.objects.count()
+
+ context = {
+ 'num_books': num_books,
+ 'num_instances': num_instances,
+ 'num_instances_available': num_instances_available,
+ 'num_authors': num_authors,
+ }
+
+ # Render the HTML template index.html with the data in the context variable
+ return render(request, 'index.html', context=context)</pre>
+
+<p>La première ligne de code permet d'importer les modèles de données du catalogue décrites dans le module <code>catalog</code>.</p>
+
+<p>La première section de la fonction index() permet à l'aide de requêtes, par l'intermédiaire des objets de modèle de données, d'obtenir les nombres d'enregistrements. Pour cela, nous utilisons la méthode d'objet <em>models</em> <code>objects.all()</code> sur les objets <code>Book</code> et <code>BookInstance</code>. En sus, nous recherchons les ouvrages disponibles, ce qui revient à faire une requête avec un filtre sur l'attribut status de l'objet <code>BookInstance</code> ayant la valeur 'a' (Available). Si vous avez un oubli, vous pouvez consulter <a href="/fr/docs/Learn/Server-side/Django/Models#Rechercher_des_enregistrements">La section 3 de Django didactique : utilisation du modèle de données &gt; Chercher des enregistrements</a>.</p>
+
+<p>La dernière ligne de cette fonction est l'appel de la fonction <code>render()</code> dont l'objet est de constituer une page HTML et la transmettre comme une réponse. Cette fonction encapsule plusieurs autres fonctions du cadriciel ce qui permet de simplifier le processus de restitution des informations. La fonction <code>render()</code> utilise les paramètres :</p>
+
+<ul>
+ <li>La requête HTTP initiale <code>request</code> qui est un objet de type <code>HttpRequest</code>.</li>
+ <li>Un gabarit de page HTML avec des zones prédéfinies pour les données.</li>
+ <li>Un contexte de variables (<code>context)</code> qui est dictionnaire en Python, contenant les données à insérer dans le gabarit pour publier la page HTML. </li>
+</ul>
+
+<p>Nous aborderons plus en détail les aspects de gabarit et de contexte des variables dans le section suivante du didacticiel. Pour le moment, construisons un premier gabarit sans plus de précisions.</p>
+
+<h3 id="Gabarit_(Template)">Gabarit (<em>Template</em>)</h3>
+
+<p>Un gabarit est un fichier texte qui décrit la structure ou la mise en page d'un document (comme une page HTML) et qui utilise des emplacements réservés pour y insérer des informations issues de la base de données.</p>
+
+<p>La cadriciel Django va rechercher automatiquement ces gabarits dans un répertoire nommé '<strong>templates</strong>' dans le dossier de l'application. Si vous reprenez la dernière ligne de la de fonctions <code>index()</code> dans l'exemple ci-dessus, la fonction <code>render()</code> a besoin du fichier <em><strong>index.html</strong></em> qui devrait être placé dans le dossier<em> </em><strong>/locallibrary/catalog/templates/</strong>. Dans le cas contraire, cela génère une erreur car le fichier est considéré comme absent.</p>
+
+<p>Vous pouvez en faire l'expérience dès à présent, après avoir redémarré votre serveur local, en accédant à l'URL <code>127.0.0.1:8000</code> de votre navigateur. Une page d'erreur explicite s'affiche en indiquant un message du type : "<code>TemplateDoesNotExist at /catalog/</code>", ainsi que de nombreux détails sur l'enchaînement des fonctions aboutissant à cette erreur.</p>
+
+<div class="note">
+<p><strong>Note</strong>: En fonction du paramétrage de votre projet - le fichier settings.py de votre projet - Django va chercher pour des gabarits dans différents répertoires et dans ceux de votre application par défaut. Si vous souhaitez approfondir ce sujet vous pouvez consulter la <a href="https://docs.djangoproject.com/fr/2.2/topics/templates/">documentation Django relative aux gabarit</a>.</p>
+</div>
+
+<h4 id="Concevoir_les_gabarits">Concevoir les gabarits</h4>
+
+<p>Django utilise un langage de pour les gabarit qui permet de résoudre certains sujets liés au page HTML. En l'occurrence, dans le site web de la bibliothèque nous aurons des bandeaux de navigateur et autres codes d'en-tête à réutiliser. Dans une vision classique, il faudrait récrire dans chaque page le même code pour obtenir le même rendu. Si cela peut se concevoir pour quelques pages, ce procédé devient vite inopérant voir risqué avec un site dynamique complet.</p>
+
+<p>Le langage de gabarit de Django permet de définir un modèle de base puis de l'étendre ensuite. L'extrait de code ci-dessous vient du fichier de gabarit <strong>base_generic.html</strong>, vous constaterez qu'il s'y mélange du code HTML et des sections nommées contenu dans entre des marqueurs <code>block</code> et <code>endblock</code> qui peut contenir ou non des données.</p>
+
+<div class="note">
+<p><strong>Note</strong>: <span class="tlid-translation translation" lang="fr"><span title="">Les marqueurs de gabarits sont des fonctions que vous pouvez utiliser dans un modèle pour parcourir des listes, effectuer des opérations conditionnelles en fonction de la valeur d'une variable, etc.</span> <span title="">Outre les balises de modèle, la syntaxe de gabarit vous permet de référencer les variables qui sont transmises au modèle à partir de la vue et d'utiliser des filtres de gabarit pour mettre en forme les variables (par exemple, pour convertir une chaîne en minuscule).</span></span></p>
+</div>
+
+<p>Dans l'extrait ci-dessous vous avec trois sections nommées qui pourront être remplacés par la suite :</p>
+
+<ul>
+ <li>title : qui contiendra le titre (par défaut Bibliothèque locale)</li>
+ <li>sidebar : une barre de navigation latérale à gauche</li>
+ <li>content : le contenu de la page</li>
+</ul>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="fr"&gt;
+&lt;head&gt;
+ <strong>{% block title %}</strong>&lt;title&gt;Bibliothèque locale&lt;/title&gt;<strong>{% endblock %}</strong>
+&lt;/head&gt;
+&lt;body&gt;
+ <strong>{% block sidebar %}</strong>&lt;!-- insert default navigation text for every page --&gt;<strong>{% endblock %}</strong>
+ <strong>{% block content %}</strong>&lt;!-- default content text (typically empty) --&gt;<strong>{% endblock %}</strong>
+&lt;/body&gt;
+&lt;/html&gt;
+</pre>
+
+<p>Lorsque l'on définit un gabarit pour une vue particulière, il convient de définir une base de gabarit et d'utiliser la balise <code>extends</code> dans une page complémentaire comme dans l'exemple ci-dessous.  Ensuite, il est nécessaire de préciser les sections qui seront modifiées en utilisant les balises <code>block</code>/<code>endblock</code> qui définissent le début et la fin de section.</p>
+
+<p>A titre indicatif, l'extrait ci-dessous présente la manière d'activer à l'aide de la balise <code>extends</code> le remplacement de la section <code>content</code>. La page HTML générée inclura la structure de la page définit plus haute et le code généré à la fois pour la section <code>title</code>, mais avec les éléments nouveaux, ci-dessous, pour la section <code>content</code>.</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Accueil de la bibliothèque locale&lt;/h1&gt;
+ &lt;p&gt;Bienvenue sur la bibliothèque locale, un site web développé par &lt;em&gt;Mozilla Developer Network&lt;/em&gt;!&lt;/p&gt;
+{% endblock %}</pre>
+
+<h4 id="Le_gabarit_de_base_de_la_bibliothèque">Le gabarit de base de la bibliothèque</h4>
+
+<p>Nous allons nous appuyer sur le gabarit ci-dessous pour constuire la page de base de la bibliothèque locale. Vous le constatez, il contient des éléments HTML et des blocs dédiés Django pour spécifier trois sections <code>title</code>, <code>sidebar</code>, et <code>content</code>. La section <code>title</code> contient un titre par défaut. De même la section <code>sidebar</code> contient un liens vers la liste des livres et des auteurs qui pourra être modifié ensuite.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Il y a aussi deux balises supplémentaires : <code>url</code> et <code>load static</code>. Elles seront étudiées dans le chapitre suivant.</p>
+</div>
+
+<p>Créez un nouveau fichier nommé <strong><em>base_generic.html </em></strong>dans le dossier <strong>/locallibrary/catalog/templates/</strong> (à créer aussi) et copiez-y le texte ci-dessous :</p>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;head&gt;
+ {% block title %}&lt;title&gt;Bibliothèque locale&lt;/title&gt;{% endblock %}
+ &lt;meta charset="utf-8"&gt;
+ &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
+ &lt;link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"&gt;
+ &lt;!-- Add additional CSS in static file --&gt;
+ {% load static %}
+ &lt;link rel="stylesheet" href="{% static 'css/styles.css' %}"&gt;
+&lt;/head&gt;
+&lt;body&gt;
+ &lt;div class="container-fluid"&gt;
+ &lt;div class="row"&gt;
+ &lt;div class="col-sm-2"&gt;
+ {% block sidebar %}
+ &lt;ul class="sidebar-nav"&gt;
+ &lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=""&gt;Tous les livres&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=""&gt;Tous les auteurs&lt;/a&gt;&lt;/li&gt;
+ &lt;/ul&gt;
+ {% endblock %}
+ &lt;/div&gt;
+ &lt;div class="col-sm-10 "&gt;
+ {% block content %}
+ {% endblock %}
+ &lt;/div&gt;
+ &lt;/div&gt;
+ &lt;/div&gt;
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<p>Ce gabarit fait appel - <em>en ligne 7</em> - à la bibliothèque de style <a href="http://getbootstrap.com/">Bootstrap</a> afin d'améliorer la présentation de la page. L'utilisation de Bootstrap (ou de tout autre cadriciel pour les pages web) est un moyen rapide de créer des pages bien organisées et qui s'affiche très bien sur les différents navigateurs.</p>
+
+<p>De même, ce gabarit fait appel à une feuille de style - <em>en ligne 10</em> - locale pour ajouter ou adapter des styles. Créez le fichier <strong>styles.css </strong>dans le répertoire <strong>/locallibrary/catalog/static/css/</strong> (à créer aussi) et copiez le contenu ci-dessous :</p>
+
+<pre class="brush: css">.sidebar-nav {
+ margin-top: 20px;
+ padding: 0;
+ list-style: none;
+}</pre>
+
+<h4 id="La_page_d'accueil">La page d'accueil</h4>
+
+<p>Maintenant créez le fichier HTML <strong><em>index.html</em></strong> dans le dossier <strong>/locallibrary/catalog/templates/</strong> et copiez-y le code ci-dessous. This code extends our base template on the first line, and then replaces the default <code>content</code> block for the template. </p>
+
+<pre class="brush: html line-numbers language-html"><code class="language-html">{% extends "base_generic.html" %}
+
+{% block content %}
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>h1</span><span class="punctuation token">&gt;</span></span>Accueil de la bibliothèque locale<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>h1</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>p</span><span class="punctuation token">&gt;</span><span class="tag token"><span class="punctuation token">Bienvenue à la bibliothèque locale, un site web développé par </span></span></span></code><code class="language-html"><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>em</span><span class="punctuation token">&gt;</span></span>Mozilla Developer Network<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>em</span><span class="punctuation token">&gt;</span></span>!</code><code class="language-html"><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>p</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>h2</span><span class="punctuation token">&gt;</span></span>Contenu dynamique<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>h2</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>p</span><span class="punctuation token">&gt;</span></span>La bibliothèque dispose des enregistrements suivants:<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>p</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>ul</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>li</span><span class="punctuation token">&gt;</span></span><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>strong</span><span class="punctuation token">&gt;Livres</span></span>:<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>strong</span><span class="punctuation token">&gt;</span></span> <strong>\{{ num_books }}</strong><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>li</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>li</span><span class="punctuation token">&gt;</span></span><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>strong</span><span class="punctuation token">&gt;</span></span>Copies:<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>strong</span><span class="punctuation token">&gt;</span></span> <strong>\{{ num_instances }}</strong><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>li</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>li</span><span class="punctuation token">&gt;</span></span><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>strong</span><span class="punctuation token">&gt;</span></span>Copies disponibles:<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>strong</span><span class="punctuation token">&gt;</span></span> <strong>\{{ num_instances_available }}</strong><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>li</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>li</span><span class="punctuation token">&gt;</span></span><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;</span>strong</span><span class="punctuation token">&gt;</span></span>Auteurs:<span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>strong</span><span class="punctuation token">&gt;</span></span> <strong>\{{ num_authors }}</strong><span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>li</span><span class="punctuation token">&gt;</span></span>
+ <span class="tag token"><span class="tag token"><span class="punctuation token">&lt;/</span>ul</span><span class="punctuation token">&gt;</span></span>
+{% endblock %}</code></pre>
+
+<p>Dans la section cont'enu dynamique, des emplacements réservés sont définis pour pouvoir y insérer le contenu de variable qui sont identifiées à l'intérieur de doubles accolades (ouvrantes et fermantes). Pour une meilleure visibilité ces emplacements et les variables nommées sont identifiées en caractères gras dans l'extrait de code ci-dessus. </p>
+
+<div class="note">
+<p><strong>Note:</strong> Vous pouvez constatez simplement que les balises de gabarit (fonctions) et les baslises de variables sont entre accolades ; double accolades pour uen variable (<code>\{{ num_books }}</code>), et simple accolade avec le pourcentage (<code>{% extends "base_generic.html" %}</code>) pour les balises.</p>
+</div>
+
+<p>Gardez en mémoire que les variables utilisées dans les gabarits sont des clés d'un dictionnaires <code>context</code> transmis à la fonction <code>render()</code> de la vue (revenez à l'exemple plus haut, ou l'extrait ci-dessous). La fonction <code>render()</code> traitera le dictionnaire pour restituer uen page HTML où les variables nommées auront été remplacées par leur valeur dans le dictionnaire.</p>
+
+<pre class="brush: python">context = {
+ '<strong>num_books</strong>': num_books,
+ '<strong>num_instances</strong>': num_instances,
+ '<strong>num_instances_available</strong>': num_instances_available,
+ '<strong>num_authors</strong>': num_authors,
+}
+
+return render(request, 'index.html', context=context)</pre>
+
+<h4 id="Traiter_les_fichiers_statiques_dans_les_gabarits">Traiter les fichiers statiques dans les gabarits</h4>
+
+<p>Vos projets utiliserons probablement de fichiers statiques, par exemple des images, des fichiers de styles CSS ou des fonctions javascripts. L'emplacement de ces fichiers n'est pas connu a priori ou peut changer en fonction de l'emplacement dans un projet Django. Pour cela, Django vous permet de spécifier les emplacements dans les gabarits par rapport à la variable globale du projet <code>STATIC_URL</code>. Le paramétrage par défaut du site web définit la variable <code>STATIC_URL</code> à '<code>/static/</code>', mais vous pouvez choisir d'heberger ailleurs.</p>
+
+<p>Au sein du gabarit, vous ferrez appel à la balise <code>load</code> en précisant "static" pour faire votre ajout, comme décrits dans l'extrait ci-dessous. Vous utilisez la balise <code>static</code> et vous spécifiez ensuite l'URL pour accéder au fichier nécessaire.</p>
+
+<pre class="brush: html">&lt;!-- Add additional CSS in static file --&gt;
+{% load static %}
+&lt;link rel="stylesheet" href="{% static 'css/styles.css' %}"&gt;</pre>
+
+<p>De la même manière, vous pouvez par exemple :</p>
+
+<pre class="brush: html">{% load static %}
+&lt;img src="{% static 'catalog/images/local_library_model_uml.png' %}" alt="UML diagram" style="width:555px;height:540px;"&gt;
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Les exemples ci-dessus indiquent où se trouvent les fichiers, mais le cadriciel ne travail pas ainsi par défaut. Nous avons configuré le serveur web de développement en modifiant le routage des URL (<strong>/locallibrary/locallibrary/urls.py</strong>) à <a href="/fr/docs/Learn/Server-side/Django/skeleton_website">la création du squelette du site</a>. Cependant nous devrons travailler plus tard la mise en production.</p>
+</div>
+
+<p>Pour plus de détails sur les fichiers statiques vous pouvez consulter la documentation Django sur <a href="https://docs.djangoproject.com/fr/2.2/howto/static-files/">la gestion des fichiers statiques</a>.</p>
+
+<h4 id="Associer_des_URL">Associer des URL</h4>
+
+<p>L'exemple ci-dessous introduit l'utilisation de la balise de gabarit <code>url</code>.</p>
+
+<pre class="brush: python">&lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+</pre>
+
+<p>Cette balise accepte des référence enregistrées par la fonction <code>path()</code> appelée dans les fichiers  <strong>urls.py</strong> ainsi que les valeurs pour chacun des arguments associés à une vue. Elle retourne une URL qui peut être utilisée pour lier une ressource.</p>
+
+<h4 id="Où_trouver_les_gabarits...">Où trouver les gabarits...</h4>
+
+<p>Par défaut Django ne sait pas où sont vos gabarits, vous devez lui indiquer où les trouver. Pour faire cela, vous allez ajouter le répertoire des gabarits (templates) à la variable d'environnemet du projet TEMPLATES en éditant le fichier <strong>settings.py</strong> comme indiqué en gras ci-dessous :</p>
+
+<pre class="brush: python">TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [
+<strong>            os.path.join(BASE_DIR, 'templates'),
+</strong>        ],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]</pre>
+
+<h2 id="A_quoi_cela_devrait-il_ressembler">A quoi cela devrait-il ressembler ?</h2>
+
+<p>A ce niveau, nous avons créé l'ensemble des ressources nécessaires à l'affichage de la page d'accueil. Démarrez le serveur (<code>python3 manage.py runserver</code>) et accédez avec votre navigateur à la page d'accueil du site web <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>. Si tout va bien, vous devriez avoir une page qui ressemble à celle ci-dessous.</p>
+
+<p><img alt="Page d'accueil en Français" src="https://mdn.mozillademos.org/files/16794/index_fr_page_ok.png" style="height: 1050px; width: 1872px;"></p>
+
+<div class="note">
+<p><strong>Note:</strong> Toutes les ressources n'ayant pas été encore créées les liens vers Tous les livres et Tous les auteurs ne fonctionnent pas encore.</p>
+</div>
+
+<h2 id="Défi">Défi</h2>
+
+<p>Voici deux suggestion pour tester votre connaissance de Django et des requêtes, vues et gabarits :</p>
+
+<ol>
+ <li>La bibliothèque locale dispose d'un gabarit d'origine qui inclu une section <code>title</code>. Surchargez cette section dans le gabarit index et créer un nouveau titre.
+
+ <div class="note">
+ <p><strong>Hint:</strong> La section Concevoir un gabarit détaille la manière de modifier une section.</p>
+ </div>
+ </li>
+ <li>Modifiez la vue pour disposer de décomptes pour les genres et les titres de livre qui contiennent un mot (en repectant la casse) et transmettez cela via le  <code>context.</code> Pour faire cela utilisez les variables <code>num_books</code> et <code>num_instances_available</code>. Ensuite vous pourrez mettre à jour le gabarit de la page d'accueil.<br>
+  </li>
+</ol>
+
+<ul>
+</ul>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Dans ce chapitre, nous avons créé la page d'accueil pour notre site — une page web dynamique qui affiche le décompte d'enregistrements issus de la base de données et des liens vers des pages encire à créer. Au cours des étapes de création, nous avons appris et découvert des concept fondamentaux à propos du routage d'url, des vues des requêtes à la base de données et le passage de données vers les gabarits ainsi que leur conception.</p>
+
+<p>Nous allons nous appuyer sur ces éléments pour concevoir dans le prochain chapitres les 4 pages qui manquent.</p>
+
+<h2 id="À_voir_aussi">À voir aussi</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/intro/tutorial03/">Ecrire sa première application Django, 3ème partie</a>  (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/topics/http/urls/">Distribution des URL</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/topics/http/views/">Ecriture des vues</a> (DJango docs)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.é/topics/templates/">Gabarits</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/howto/static-files/">Gestion des fichiers statiques</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/topics/http/shortcuts/#django.shortcuts.render">Fonctions raccourcis de Django</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}</p>
+
+<h2 id="Dans_ce_module">Dans ce module</h2>
+
+<ul>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Didactique: Site web "Bibliothèque locale"</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Django didactique Section 2: Créer le squelette du site web</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Models">Django didactique Section 3: Utilisation des modèles de données</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Admin_site">Django didactique Section 4 : Site d'administration de Django</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Home_page">Django didactique Section 5: Créer la page d'accueil</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/fr/learn/server-side/django/index.html b/files/fr/learn/server-side/django/index.html
new file mode 100644
index 0000000000..c613f07883
--- /dev/null
+++ b/files/fr/learn/server-side/django/index.html
@@ -0,0 +1,65 @@
+---
+title: Django Web Framework (Python)
+slug: Learn/Server-side/Django
+tags:
+ - Apprendre
+ - Débutant
+ - Python
+ - django
+translation_of: Learn/Server-side/Django
+---
+<div>{{LearnSidebar}}</div>
+
+<p>Django est une infrastructure d'application (aussi appelé framework) côté serveur extremement populaire et dotée de beaucoup de fonctionnalités, écrite en Python. Ce module vous montrera pourquoi Django fait partie des frameworks web les plus populaires ainsi que comment l'installer, le mettre en place, et s'en servir afin de créer vos propres applications web.</p>
+
+<h2 id="Prerequis">Prerequis</h2>
+
+<p>Aucune connaissance sur ce framework n'est requise. Il vous faudra seulement comprendre ce qu'est la programmation web côté serveur ainsi que les frameworks web, notamment en lisant les sujets sur notre <a href="https://developer.mozilla.org/fr/docs/Learn/Server-side/First_steps">module d'initiation à la programmation web coté serveur</a>.</p>
+
+<p>Une connaissance générale en programmation et plus précisement en <a href="https://developer.mozilla.org/fr/docs/Glossaire/Python">Python</a> est recommandée, mais pas nécessaire pour comprendre la majeure partie de ce module.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Python est un des languages les plus faciles à apprendre, lire et comprendre pour les novices. Ceci dit, si vous voulez mieux comprendre ce module, il existe beaucoup de livres gratuits et de tutoriaux sur internet (les nouveaux programmeurs pourraient être intéressés par la page du<a href="https://wiki.python.org/moin/BeginnersGuide/NonProgrammers"> Python pour les non-programmeurs</a> <sub><strong>(anglais)</strong></sub> dans la documentation sur le site officiel de Python: python.org).</p>
+</div>
+
+<h2 id="Guides">Guides</h2>
+
+<dl>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Introduction à Django<sub><strong> </strong></sub></a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Dans ce premier article, nous répondrons aux questions "qu'est ce que Django ?" et vous donner un aperçu rapide de ce qu'un framework peut vous apporter. Nous survolerons les fonctionnalités principales ainsi que quelques fonctionnalités avancées que nous ne pouvons pas détailler en l'espace d'un seul module.  Nous vous montrerons aussi les blocs principaux de Django ce qui vous donnera un aperçu de ce qui est faisable avant de commencer.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Installer un environnement de développement pour Django </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Maintenant que vous savez ce qu'est Django, nous allons nous attaquer à la partie installation, comment l'installer sous Windows, Linux(Ubuntu),  et Mac OS X — tant que vous utilisez un système d'exploitation commun, cet article devrait vous donner le nécessaire afin de commencer à développer des applications avec Django.</dd>
+ <dt><a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">Tutoriel Django: Le site web d'une librairie</a></dt>
+ <dd>Le premier article de cette série de tutoriels explique ce que vous aurez à apprendre autour d'un site que nous allons programmer pour une bibliothèque, site web dans lequel nous allons travailler et évoluer à travers plusieurs articles.</dd>
+ <dt><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Tutoriel Django Partie 2: Créer un squelette d'un site web</a></dt>
+ <dd>Cet article vous montrera comment créer le "squelette" d'un site web auquel vous pourrez ajouter de quoi le personnaliser avec des paramètres spécifiques, des URLs, des modèles et des templates.</dd>
+ <dt><a href="/fr/docs/Learn/Server-side/Django/Models">Tutoriel Django Partie 3: Utilisation des modèles</a></dt>
+ <dd>Cet article montre comment définir des modèles pour le site web que nous appelleront <em>LocalLibrary </em> — les modèles représentent la façon dont sont structurées nos données dans nos applications, nous autoriserons aussi Django à stocker des données dans une base de données pour nous (et modifier cela plus tard). Cet article explique en somme ce qu'un modèle est, comment le déclarer et les champs principaux. Il décrit aussi brièvement comment accéder aux données d'un modèle.</dd>
+ <dt><a href="/fr/docs/Learn/Server-side/Django/Admin_site">Tutoriel Django Partie 4: L'administration d'un site sous Django</a></dt>
+ <dd>Maintenant que nous avons créé quelques modèles pour le site web <em>LocalLibrary </em>, nous allons utiliser Django Admin afin d'ajouter quelques "réelles" tables de données. Premièrement, nous allons vous montrer comment enregistrer des modèles avec la partie Admin, ensuite nous allons vous montrer comment se connecter et créer des informations. A la fin, nous allons vous montrer quelques moyens d'améliorer la présentation de la partie Admin.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Tutoriel Django Partie 5: Céez votre page d'accueil. </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Nous sommes fin prêts à ajouter le code afin d'afficher notre première page entièremement — une page d'accueil pour le site web <em>LocalLibrary </em>qui montre combien d'enregistrements nous avons de chaque types de modèles et fournis une barre de navigation avec des liens menant à d'autres pages. Au fur et à mesure, nous gagnerons de l'expérience en écrivant du mapping d'URLs, en obtenant des enregistrements de la base de données et en utilisant des templates.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Tutoriel Django Partie 6: Listes génériques et détails des pages </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Ce tutoriel viens étendre notre site <em>LocaLibrary</em> en y ajoutant des listes et des détails pour les auteurs et les livres. Ici nous allons tout vous apprendre sur les classes et vous montrer comment elles peuvent réduire la quantité de code que vous avez à écrire dans des situations communes. Nous allons aussi vous apprendre comment manipuler les URL plus en détail, ainsi que la réalisation basique d'un moteur de recherche.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Tutoriel Django Partie 7: Les sessions de framework </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Ce tutoriel viens compléter le site <em>LocalLibrary</em>, en ajoutant un compteur de visiteurs basé sur un principe de session sur la page principale C'est un exemple relativement simple, mais il vous permettra de vous apprendre comment utiliser le système de session en fournissant un comportement persistant aux utilisateurs anonyme de votre site.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Tutoriel Django Partie 8: L'authentification de l'utilisateur ainsi que les permissions </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Dans ce tutoriel, nous allons vous montrer comment autoriser les utilisateurs à se connecter à votre site avec leurs propres comptes, et comment contrôler ce qu'ils peuvent faire et voir en fonction des <em>permissions</em> accordées et de s'ils sont connectés ou non. Comme partie de cette démonstration, nous allons étendre le site <em>LocalLibrary</em> en ajoutant une page de connexion, de déconnexion et d'utilisateur - ainsi que des pages dédiées aux membres de la librairie afin de voir quel livre a été emprunté.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Forms">Tutoriel Django Partie 9: Travailler avec les formulaires </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Dans ce tutoriel, nous allons vous montrer comment travailler avec <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms">les formulaires en HTML</a> avec Django, et plus particulièrement la façon la plus facile d'écrire, créer, mettre à jour et supprimer les formulaires. Pour cela, nous allons devoir étendre le site <em>LocalLibrary</em> afin que les libraires puissent changer les livres, et créer, mettre à jour, et supprimer les auteurs en utilisant nos propres formulaires (au lieu de passer par Django Admin).</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Testing">Tutoriel Django Partie 10: Tester une application Django </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Plus les sites s'agrandissent, plus il devient dur de les tester manuellement — pas seulement parce que il y a plus de contenu à tester mais aussi parce que les intéractions entre les éléments deviennent plus complexes, un petit changement dans une partie du site peut nécessiter de nombreux tests afin de vérifier que ce changement n'a pas impacté les autres parties du site. La solution à ce problème est de programmer des tests automatiques, qui peuvent facilement et fiablement être executés à chaque changements. Ce tutoriel montre comment automatiser vos tests sur votre site web en utilisant le module de test du framework Django.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Tutoriel Django Partie 11: Déployer son site fait avec Django </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Vous avez créé (et testé) un incroyable site web <em>LocalLibray</em>, vous allez maintenant l'installer sur un serveur public ce qui le rendra accessible aux membres de la librairie à travers internet. Cet article fournis un aperçu de comment vous pourriez trouver un hébergeur pour déployer votre site et de ce dont vous avez besoin pour rendre votre site pleinement fonctionnel.</dd>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Le module de sécurité de Django </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Protéger les données de l'utilisateur est essentiel dans la conception d'un site web, nous avons précédemment expliqué quel pouvaient être les menaces principales dans l'article sur la <a href="https://developer.mozilla.org/en-US/docs/Web/Security">sécurité web</a>  — cet article fournis une démonstration pratique des réaction des protections incluse de Django face à ce genre de menaces ainsi que la façon dont elles sont traitées.</dd>
+</dl>
+
+<h2 id="Evaluation">Evaluation</h2>
+
+<p>L'évaluation suivante va tester votre compréhension à créer un site web avec Django comme décris dans la liste des guides ci-dessous.</p>
+
+<dl>
+ <dt><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">Mini blog avec Django </a><sub><strong>(anglais)</strong></sub></dt>
+ <dd>Dans ce devoir, vous utiliserez les connaissances que vous venez d'acquérir, afin de créer votre propre blog.</dd>
+</dl>
diff --git a/files/fr/learn/server-side/django/introduction/index.html b/files/fr/learn/server-side/django/introduction/index.html
new file mode 100644
index 0000000000..d4938e0610
--- /dev/null
+++ b/files/fr/learn/server-side/django/introduction/index.html
@@ -0,0 +1,277 @@
+---
+title: Introduction à Django
+slug: Learn/Server-side/Django/Introduction
+tags:
+ - Débutant
+ - Python
+ - django
+translation_of: Learn/Server-side/Django/Introduction
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}</div>
+
+<p class="summary"><span class="seoSummary">Dans ce premier article sur Django, nous allons répondre à la question suivante "Qu'est ce que Django ?", et nous vous montrerons en quoi cette infrastructure d'application (aussi appelée framework) est spéciale. Nous allons ensuite voir les principales fonctionnalités, mais aussi quelques fonctionnalités avancées que nous n'aurons malheureusement pas le temps de voir en détails dans ce module. Nous allons aussi vous montrer comment fonctionne une application Django (bien que nous n'ayons pas d'environnement où le tester) </span>.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis:</th>
+ <td>Connaissances basiques de programmation. Une compréhension générale de la <a href="https://developer.mozilla.org/fr/docs/Learn/Server-side/First_steps">programmation coté serveur</a> ainsi qu'une <a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview">compréhension des interactions client-serveur dans les sites web</a> <strong><sub>(anglais)</sub></strong>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objectif:</th>
+ <td>Se familiariser avec Django en comprenant ce que c'est, les fonctionnalités qu'il fournit ainsi que les blocs de construction principaux d'une application Django.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Quest_ce_que_Django">Qu'est ce que Django?</h2>
+
+<p>Django est un framework Python de haut niveau, permettant un développement rapide de sites internet, sécurisés, et maintenables. Créé par des développeurs experimentés, Django prend en charge la plupart des tracas du développement web, vous pouvez donc vous concentrer sur l'écriture de votre application sans avoir besoin de réinventer la roue. Il est gratuit, open source, a une communauté active, une bonne documentation, et plusieurs options pour du support gratuit ou non. </p>
+
+<p>Django vous aide à écrire une application qui est:</p>
+
+<dl>
+ <dt>Complète</dt>
+ <dd>Django suit la philosophie "Piles incluses" et fournit presque tout ce que les développeurs pourraient vouloir faire. Comme tout ce dont vous avez besoin est une partie de ce "produit", tout fonctionne parfaitement ensemble, suivant des principes de conception cohérents, il possède également une <a href="https://docs.djangoproject.com/en/2.0/">documentation complète </a>et à jour.</dd>
+ <dt>Polyvalent</dt>
+ <dd>Django peut être (et a été) utilisé pour créer presque tous les genres de sites — du gestionnaire de données aux wikis, jusqu'aux réseaux sociaux et aux sites d'actualités. Il peut fonctionner avec n'importe quelle infrastructure côté client, et peut renvoyer des données dans quasiment n'importe quel format (notamment HTML, RSS, JSON, XML, etc). Le site sur lequel vous lisez en ce moment est basé sur Django!<br>
+ <br>
+ Tandis qu'il fournit presque toutes les fonctionnalités dont vous pourriez avoir besoin (comme des base de données populaires, des moteurs de modélisation, etc.), il peut tout de même être étendu pour utiliser d'autres composants si besoin.</dd>
+ <dt>Sécurisé</dt>
+ <dd>Django aide les développeurs à éviter les erreurs de sécurité classique en fournissant une infrastructure conçue pour "faire ce qu'il faut" pour protéger les sites internet automatiquement. Par exemple, Django fournit un moyen sécurisé pour gérer les comptes des utilisateurs ainsi que leurs mots de passe, évitant les erreurs classiques comme mettre des informations sur la session dans des cookies, où elles sont vulnérables (à la place les cookies contiennent seulement une clé, et les données sont stockées dans la base de données), ou directement stocker des mots de passe, au lieu de mot de passe hachés.<br>
+ <br>
+ <em>Un mot de passé haché est une valeur dont la longueur est fixée, créée en envoyant le mot de passe à travers une <a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function">fonction de hachage cryptographique</a>. Django peut vérifier si un mot de passe entré est correct en l'envoyant dans la fonction de hachage et en comparant le retour avec la valeur stockée dans la base de données. De ce fait, la nature unidirectionnelle de la fonction rend difficile pour un attaquant de retrouver le mot de passe d'origine, même si la valeur hachée est compromise.</em><br>
+ <br>
+ Django active par défaut la protection contre beaucoup de vulnérabilités, comme les injections SQL, le cross-site scripting, le cross-site request forgery et le clickjacking (voir <a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">Website security</a> pour plus de détails sur ce genre d'attaques).</dd>
+ <dt>Scalable</dt>
+ <dd>Django utilise une architecture composite "shared-nothing" (chaque composant de l'architecture est indépendant des autres, et peut ainsi être remplacé ou changé si besoin). En ayant des séparations nettes entres les différentes parties, Django peut se scaler lors d'une hausse de trafic en ajoutant du hardware à tous les niveaux : serveurs cache, serveurs de base de données, serveurs d'application. Certains des sites les plus fréquentés ont réussi à scaler Django pour répondre à leur demande (par exemple, Instagram et Disqus pour ne nommer qu'eux deux).</dd>
+ <dt>Maintenable</dt>
+ <dd>Les principes de design du code Django encouragent la création d'un code simple à maintenir et réutilisable. Il fait notamment appel à la philosophie du Ne Vous Répétez Pas (DRY pour Don't Repeat Yourself en anglais), afin d'éviter toute duplication superflue, réduisant la taille de votre code. Django promeut aussi le regroupement de fonctionnalités reliées entre elles en "applications" réutilisables et, à un plus bas niveau, regroupe des lignes de code dépendantes entre elles en modules (suivant les lignes du motif d'architecture Modèle-vue-contrôleur (MVC)).</dd>
+ <dt>Portable</dt>
+ <dd>Django est écrit en Python, qui fonctionne sous diverses plateformes. Cela veut dire que vous ne serez plus contraint par une plateforme en particulier, et vous pourrez faire fonctionner vos applications sous autant de versions de Linux, Windows et Mac OS X que vous le souhaitez. De plus, Django est très bien supporté par plusieurs fournisseurs d'hébergement web, qui offrent souvent des infrastructures et de la documentation spécifiques pour héberger des sites Django.</dd>
+</dl>
+
+<h2 id="Doù_vient-il">D'où vient-il ?</h2>
+
+<p>À l'origine, Django fut développé entre 2003 et 2005 par une équipe web responsable de la création et de la maintenance de sites journalistiques. Après avoir créé un certain nombre de sites, l'équipe commença à exclure et à réutiliser des codes récurrents et des schémas d'architecture. Ce code récurrent finit par évoluer en un framework générique de développement web, qui fut mis à disposition en open-source sous le projet "Django" en Juillet 2005.</p>
+
+<p>Django a continué à se développer et à s'améliorer, depuis sa première sortie (1.0) en Septembre 2008 jusqu'à la version 2.0 récemment sortie (2017). Chaque sortie a ajouté son lot de nouvelles fonctionnalités et de corrections de bugs, allant du support de nouveaux types de bases de données, de moteurs de templates et de cache, à l'addition de fonctions et de classes de vues 'génériques' (qui réduisent la quantité de code que doivent écrire les développeurs pour tout un tas de tâches de programmation). </p>
+
+<div class="note">
+<p><strong>Note </strong>: Consultez les<span style="line-height: 1.5;"> <a href="https://docs.djangoproject.com/en/1.10/releases/">notes de publication</a> sur le site web de Django pour voir les changements apportés dans les versions récentes, ainsi que tout le travail accompli pour améliorer Django.</span></p>
+</div>
+
+<p>Désormais, Django est un projet open-source collaboratif florissant, avec plusieurs milliers d'utilisateurs et de contributeurs. Bien que plusieurs fonctionnalités reflètent encore ses origines, Django a évolué en un framework versatile capable de développer n'importe quel type de site web.<span style="line-height: 1.5;"> </span></p>
+
+<h2 id="À_quel_point_Django_est-il_populaire">À quel point Django est-il populaire ?</h2>
+
+<p>Il n'y a pas encore de mesure toute prête et définitive de la popularité des frameworks orientés serveur (bien que des sites comme <a href="http://hotframeworks.com/">Hot Frameworks</a> tentent d'estimer cette popularité en utilisant des moyens comme le comptage de projets GitHub et de questions sur StackOverflow pour chaque plateforme). Une meilleure question serait plutôt est-ce que Django est "suffisamment populaire" pour éviter les problèmes des plateformes moins populaires. Va-t-il continuer d'évoluer ? Pourrez-vous obtenir de l'aide si vous en avez besoin ? Aurez-vous des opportunités d'emploi si vous apprenez Django ? </p>
+
+<p>Si l'on se base sur la quantité de sites web reconnus qui utilisent Django, la quantité de personnes contribuant à son code source, et la quantité de personnes fournissant du support libre ou payant, alors oui, Django est un framework populaire !</p>
+
+<p>Parmi les sites web qui utilisent Django, on retrouve : Disqus, Instagram, la Knight Foundation, la MacArthur Foundation, Mozilla, National Geographic, l'Open Knowledge Foundation, Pinterest et Open Stack (source : <a href="https://www.djangoproject.com/">Page d'accueil de Django</a>).</p>
+
+<h2 id="Django_est-il_restrictif">Django est-il restrictif ?</h2>
+
+<p>Les frameworks web sont souvent qualifiés de "restrictifs" ou de "non-restrictifs".</p>
+
+<p>Les frameworks restrictifs sont ceux vous donnant une "bonne façon" de réaliser une tâche particulière. Ils sous-tendent souvent un développement rapide <em>dans un domaine particulier </em>(résoudre des problèmes d'un type particulier), car cette bonne façon de faire est souvent bien comprise et bien documentée. Cependant, ils peuvent être moins flexibles dans leurs capacités à résoudre des problèmes en dehors de leur domaine, et offrent souvent moins de choix sur les composants et approches utilisables.</p>
+
+<p>En contraste, les frameworks non-restrictifs ont moins de restrictions sur la meilleure façon d'assembler des composants ensemble pour achever un but, voire même sur quels composants utiliser. Ils simplifient la tâche des développeurs en leur permettant d'utiliser les outils les mieux adaptés à la réalisation d'une tâche particulière, au coût toutefois du besoin du développeur de trouver ces composants.<br>
+ <br>
+ Django est "plus ou moins restrictif", et offre ainsi le "meilleur de chaque approche". Il fournit un ensemble de composants pour gérer la plupart des tâches de développement web ainsi qu'une (ou deux) approches préférées sur leur utilisation. Toutefois, l'architecture découplée de Django implique que vous pouvez généralement choisir parmi un certain nombre d'options différentes, ou bien fournir un support pour des approches complètement nouvelles si vous le désirez.</p>
+
+<h2 id="À_quoi_ressemble_le_code_Django">À quoi ressemble le code Django ?</h2>
+
+<p>Dans un site web traditionnel orienté-données, une application web attend une requête HTTP d'un navigateur web (ou tout autre client). Quand une requête est reçue, l'application en comprend les besoins d'après l'URL et parfois d'après les informations en <code>POST</code> data ou <code>GET</code> data. En fonction de ce qui est attendu, elle peut ensuite lire ou écrire l'information dans une base de données ou réaliser une autre tâche requise pour satisfaire la requête. L'application renvoie ensuite une réponse au navigateur web, créant souvent en dynamique une page HTML affichée dans le navigateur où les données récupérées sont insérées dans les balises d'un modèle HTML.</p>
+
+<p>Les applications web Django regroupent généralement le code qui gère chacune de ces étapes dans des fichiers séparés :</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13931/basic-django.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<ul>
+ <li><strong>URLs : </strong> Bien qu'il soit possible de traiter les requêtes de chaque URL via une fonction unique, il est bien plus viable d'écrire une fonction de vue isolée qui gèrera chaque ressource. Un mapper URL est utilisé pour rediriger les requêtes HTTP à la vue appropriée d'après l'URL de requête. Le mapper URL peut aussi faire la correspondance entre des patterns de chaînes de caractères ou de nombres qui apparaissent dans une URL et passer ces derniers comme données dans une fonction de vue.</li>
+ <li><strong>Vues :</strong> Une vue est une fonction de gestion des requêtes, qui reçoit des requêtes HTTP et renvoie des réponses HTTP. Les vues accèdent aux données requises pour satisfaire des requêtes via des <em>modèles</em>, et délèguent le formatage des réponses aux <em>templates</em>.</li>
+ <li><strong>Modèles :</strong> Les modèles sont des objets Python, qui définissent la structure des données d'une application, et fournissent des mécanismes de gestion (ajout, modification, suppression) et requêtent les enregistrements d'une base de données. </li>
+ <li><strong>Templates:</strong> Un template est un fichier texte qui définit la structure ou la mise en page d'un fichier (comme une page HTML), avec des balises utilisées pour représenter le contenu. Une <em>vue </em>peut créer une page HTML en dynamique en utilisant un template HTML, en la peuplant avec les données d'un <em>modèle</em>. Un template peut-être utilisé pour définir la structure de n'importe quel type de fichier; il n'est pas obligatoire que ce dernier soit un HTML !</li>
+</ul>
+
+<div class="note">
+<p><strong>Note</strong>: Django mentionne cette organisation sous le nom d'architecture "Modèle Vue Template". Elle a plusieurs similarités avec l'architecture <a href="/en-US/docs/Web/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">Modèle Vue Contrôleur</a>.</p>
+</div>
+
+<ul>
+</ul>
+
+<p>Les sections ci-dessous vous donneront une idée de ce à quoi ressemble ces différentes parties d'une application Django (nous verrons plus de détails plus tard dans le jeu, une fois que nous aurons configuré l'environnement de développement).</p>
+
+<h3 id="Envoyer_la_requête_à_la_bonne_vue_urls.py">Envoyer la requête à la bonne vue (urls.py)</h3>
+
+<p>Le mapper URL est généralement stocké dans un fichier nommé <strong>urls.py</strong>. Dans l'exemple ci-dessous, le mapper (<code>urlpatterns</code>) définit une liste de mappings entre des <em>routes</em> (des <em>patterns </em>d'URL spécifiques<em>) </em>et leur fonction de vue correspondante. Si une requête HTTP est reçue dont l'URL correspond à un pattern spécifié, la fonction vue associée sera alors appelée et passée dans la requête.</p>
+
+<pre>urlpatterns = [
+ <strong>path('admin/', admin.site.urls),
+  </strong>path('book/&lt;int:id&gt;/', views.book-detail, name='book-detail'),
+ path('catalog/', include('catalog.urls')),
+ re_path(r'^([0-9]+)/$', views.best),
+]
+</pre>
+
+<p>L'objet <code>urlpatterns</code> est une liste de fonctions <code>path()</code> et/ou <code>re_path()</code>(les listes en Python sont définies en utilisant des crochets), où des éléments sont séparés par des virgules et peuvent avoir une <a href="https://docs.python.org/2/faq/design.html#why-does-python-allow-commas-at-the-end-of-lists-and-tuples">virgule de traîne optionnelle</a>. Par exemple : <code>[item1, item2, item3,]</code>).</p>
+
+<p>Le premier argument de chaque méthode est une route (pattern) qui sera reconnu.<br>
+ La méthode <code>path()</code> utilise des chevrons pour définir les parties de l'URL qui seront capturées et passées dans les fonctions vues comme arguments nommés. La fonction <code>re_path()</code> utilise une approche de correspondance de pattern flexible, connue sous le nom d'expression régulière. Nous parlerons de ces dernières dans un prochain article !</p>
+
+<p>Le second argument est une autre fonction qui sera appelée quand le pattern sera reconnu. La notation <code>views.book-detail</code> indique que la fonction s'appelle <code>book-detail()</code> , et qu'elle se trouve dans un module appelé <code>views</code> (i.e. dans un fichier intitulé <code>views.py</code>)</p>
+
+<h3 id="Traiter_la_requête_views.py">Traiter la requête (views.py)</h3>
+
+<p>Les vues sont le coeur des applications web. Elles reçoivent des requêtes HTTP de clients web et renvoient des réponses HTTP. Entretemps, elles mobilisent les autres ressources du framework pour accéder aux bases de données, préparer le rendu des templates, etc.</p>
+
+<p>L'exemple ci-dessous montre une fonction vue minimale <code>index()</code>, qui pourrait être appelée par notre mapper URL de la section précédente. Comme toutes les fonctions vues, elle reçoit un objet  <code>HttpRequest</code> comme paramètre (<code>request</code>) et renvoie un objet <code>HttpResponse</code>. Dans notre cas on ne fait rien de spécial avec la requête; et notre réponse ne renvoie qu'une chaîne de caractères brute. Nous vons montrerons une requête plus intéressante dans une autre section.</p>
+
+<pre class="brush: python">## nom du fichier : view.py (fonction vue Django)
+
+from django.http import HttpResponse
+
+def index(request):
+ # Reçoit une HttpRequest - le paramètre request
+ # réalise des opérations en utilisant les informations de la requête
+ # Renvoie HttpResponse
+ return HttpResponse('Hello from Django!')
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Un peu de Python :</p>
+
+<ul>
+ <li>Les <a href="https://docs.python.org/3/tutorial/modules.html">modules Python</a> sont des librairies de fonctions, stockés dans des fichiers séparés que l'on peut vouloir utiliser dans notre code. Ici, nous importons l'objet  <code>HttpResponse</code> du module <code>django.http</code> pour qu'on puisse l'utiliser dans notre vue : <code>from django.http import HttpResponse</code> . Il y a d'autres façons d'importer quelques objets (ou tous les objets) d'un module.</li>
+ <li>Les fonctions sont déclarées en utilisant le mot-clé <code>def</code> comme indiqué ci-dessus, avec des paramètres nommés listés entre parenthèses après le nom de la fonction; la ligne se termine ensuite par deux points. Notez que les lignes suivantes sont <strong>indentées</strong>.  L'indentation est importante, car elle spécifie que les lignes de code sont contenues dans un bloc particulier (l'indentation obligatoire est un élément clé de Python, et une des raisons pour lesquelles le code Python est si simple à lire).</li>
+</ul>
+</div>
+
+<ul>
+</ul>
+
+<p>Les vues sont généralement stockées dans un fichier nommé <strong>views.py</strong>.</p>
+
+<h3 id="Définir_les_modèles_de_données_models.py">Définir les modèles de données (models.py)</h3>
+
+<p>Les applications web Django gèrent et requêtent les données via des objets Python appelés modèles. Les modèles définissent la structure des données stockées, ce qui inclut le champ <em>types</em> ainsi qu'au besoin leur taille maximum, les valeurs par défaut, les options de listes pouvant être sélectionnées, le texte d'aide pour la documentation — vous pouvez choisir ce dont vous avez besoin par rapport aux spécifications de votre projet. Une fois que vous avez choisi la base de données que vous souhaitez utiliser, vous n'avez pas du tout besoin de communiquer avec elle directement — vous n'avez qu'à écrire la structure de votre modèle, Django s'occupe du sale boulot de la communication avec la base de données pour vous.</p>
+
+<p>L'extrait de code ci-dessous montre un modèle Django très simple pour un objet <code>Team</code>. La classe <code>Team</code> est dérivée de la classe Django <code>models.Model</code>. Elle définit le nom et le niveau de l'équipe comme des chaînes de caractères et elle spécifie le nombre maximum de caractères pouvant être stockés pour chaque enregistrement. Le champ <code>team_level</code> peut avoir plusieurs valeurs, donc nous le définissons comme une liste de choix, puis on fournit à la classe un mapping entre les choix qui seront affichés et les données stockées, avec une valeur par défaut. </p>
+
+<pre class="brush: python"># nom du fichier : models.py
+
+from django.db import models
+
+class Team(models.Model):
+  team_name = models.CharField(max_length=40)
+
+    TEAM_LEVELS = (
+        ('U09', 'Under 09s'),
+        ('U10', 'Under 10s'),
+        ('U11', 'Under 11s'),
+  ... # lister les autres niveaux d'équipes
+    )
+    team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Un peu de Python :</p>
+
+<ul>
+ <li>Python supporte la "programmation orientée-objet", un type de programmation où l'on organise notre code en objets, ce qui inclut les données et fonctions liées qui agiront sur les données. Les objets peuvent être hérités/étendus/dérivés d'autres objets, ce qui permet à ces objets de partager un comportement commun. En Python, on utilise le mot-clé <code>class</code> pour définir le "squelette" d'un objet. On peut créer plusieurs <em>instances</em> spécifiques de ce type d'objet d'après le modèle d'une classe.<br>
+ <br>
+ Ainsi par exemple, nous avons ici une classe <code>Team</code>, dérivée de la classe <code>Model</code>.  Cela signifie que c'est un modèle, et qu'elle contiendra toutes les méthodes d'un modèle, mais qu'on peut aussi lui donner des caractéristiques spécifiques. Dans notre modèle, nous définissons les champs dont aura besoin notre base de données, en leur donnant des noms spécifiques. Django utilisera ces définitions, ce qui inclut aussi le nom des champs, pour créer la base de données sous-jacente.</li>
+</ul>
+</div>
+
+<h3 id="Requêter_les_données_views.py">Requêter les données (views.py)</h3>
+
+<p>Le modèle Django fournit une API de requête simplifiée qui nous permet de faire des recherches dans une base de données. Cette API peut inclure plusieurs champs à la fois en supportant plusieurs critères (e.g. exactement, insensible à la casse, supérieur à, etc.), et peut supporter des déclarations complexes (par exemple, vous pouvez spécifier une recherche sur les équipes U11 ayant un nom d'équipe commençant par "Fr" ou se terminant par "al"). </p>
+
+<p>L'extrait de code ci-dessous montre une fonction vue (gestionnaire de ressources) affichant toutes nos équipes U09. La ligne en gras montre comment on peut utiliser l'API de requête pour filtrer tous les enregistrements où le champ <code>team_level</code> comprend strictement le texte 'U09' (notez comment ce critère est passé dans la fonction <code>filter()</code> comme argument, où le nom du champ et le type de correspondance sont séparés par un double underscore : <strong>team_level__exact</strong>).</p>
+
+<pre class="brush: python">## nom du fichier : views.py
+
+from django.shortcuts import render
+from .models import Team
+
+def index(request):
+    <strong>list_teams = Team.objects.filter(team_level__exact="U09")</strong>
+    context = {'youngest_teams': list_teams}
+    return render(request, '/best/index.html', context)
+</pre>
+
+<dl>
+</dl>
+
+<p>Cette fonction utilise la fonction <code>render()</code> pour créer la <code>HttpResponse</code> qui est renvoyée au navigateur. Cette fonction est un <em>raccourci</em>; elle créée un fichier HTML en combinant un template HTML spécifique et des données à insérer dans le template (fournies dans la variable appelée "<code>context</code>"). Dans la prochaine section, nous vous montrons comment des données sont insérées dans le template pour générer le HTML.</p>
+
+<h3 id="Renvoyer_les_données_templates_HTML">Renvoyer les données (templates HTML)</h3>
+
+<p>Les systèmes template vous permettent de spécifier la structure d'un document en output, en utilisant des paramètres fictifs qui seront substitués par les données lorsque la page est générée. Les templates sont souvent utilisées pour créer du HTML, mais ils peuvent aussi être utilisées pour créer d'autres types de documents. Django supporte à la fois son système natif de template ainsi qu'une autre librairie Python populaire prête à l'emploi appelée Jinja2 (il peut aussi supporter d'autres systèmes au besoin). </p>
+
+<p>L'extrait de code ci-dessous montre à quoi pourrait ressembler le template HTML de la section précédente une fois appelé par la fonction <code>render().</code> Ce template a été écrit avec l'hypothèse qu'il aurait accès à une liste de variables appelées <code>youngest_teams</code> lorsqu'il est généré (contenu dans la variable <code>context</code> dans la fonction <code>render()</code> ci-dessus). Dans le squelette HTML nous avons une expression qui vérifie tout d'abord que la variable <code>youngest_teams</code> existe, puis itère dessus dans une boucle <code>for</code> . À chaque itération, le template affiche la valeur du <code>team_name</code> de chaque équipe dans un élément {{htmlelement("li")}}.</p>
+
+<pre class="brush: python">## nom du fichier : best/templates/best/index.html
+
+&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;body&gt;
+
+ {% if youngest_teams %}
+    &lt;ul&gt;
+    {% for team in youngest_teams %}
+        &lt;li&gt;\{\{ team.team_name \}\}&lt;/li&gt;
+    {% endfor %}
+    &lt;/ul&gt;
+{% else %}
+    &lt;p&gt;No teams are available.&lt;/p&gt;
+{% endif %}
+
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<h2 id="Que_pouvez-vous_faire_dautre">Que pouvez-vous faire d'autre ?</h2>
+
+<p>Les sections précédentes présentent les caractéristiques principales que vous utiliserez dans presque toutes vos applications web : mapping URL, vues, modèles et templates. Parmi les autres caractéristiques offertes par Django, on peut aussi trouver :</p>
+
+<ul>
+ <li><strong>Formulaires </strong>: Les formulaires HTML sont utilisés pour collecter des données utilisateurs qui seront traitées sur le serveur. Django simplifie la création, la validation et le traitement des formulaires.</li>
+ <li><strong>Authentification et permissions des utilisateurs</strong>: Django inclut un système d'authentification utilisateur et de gestion des permissions robuste créé avec la sécurité comme priorité lors de sa conception. </li>
+ <li><strong>Cache </strong>: Générer du contenu en dynamique demande bien plus de ressources computationnelles (et est plus lent) que de servir du contenu statique. Django fournit un système de cache flexible qui vous permet de stocker toute ou une partie d'une page afin qu'elle ne soit re-générée que lorsque c'est nécessaire.</li>
+ <li><strong>Administration du site </strong>: L'administration du site avec Django est incluse par défaut lorsque vous créez une application en utilisant le squelette de base. Django permet de créer très simplement une page d'administration où les administrateurs peuvent créer, éditer et voir n'importe quel modèle de données sur votre site.</li>
+ <li><strong>Sérialisation des données </strong>: Django permet de simplifier la sérialisation et de servir vos données en XML ou en JSON. Cela peut être utile si vous créez un service web (un site web dont le seul but est de servir des données qui seront utilisées par d'autres applications ou sites, mais n'affiche rien par lui-même), ou quand vous créez un site web où le code côté client s'occupe d'afficher les données.</li>
+</ul>
+
+<h2 id="Sommaire">Sommaire</h2>
+
+<p>Félicitations, vous avez atteint la première étape dans votre voyage avec Django ! Vous devriez désormais comprendre les principaux bénéfices de Django, en savoir un peu plus sur son histoire, et grossièrement à quoi ressemblent chaque partie de votre application Django. Vous devriez aussi avoir appris 2-3 choses à propos du langage de programmation Python, ce qui inclut la syntaxe des listes, fonctions et classes.</p>
+
+<p>Vous avez déjà vu un peu de vrai code Django ci-dessus, mais à la différence du code côté client, vous aurez besoin de configurer un environnement de développement pour l'utiliser. C'est notre prochaine étape.</p>
+
+<div>{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}</div>
+
+<h2 id="In_this_module">In this module</h2>
+
+<ul>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Didactique: Site web "Bibliothèque locale"</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Django didactique Section 2: Créer le squelette du site web</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Models">Django didactique Section 3: Utilisation des modèles de données</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Admin_site">Django didactique Section 4 : Site d'administration de Django</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Home_page">Django didactique Section 5: Créer la page d'accueil</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/fr/learn/server-side/django/models/index.html b/files/fr/learn/server-side/django/models/index.html
new file mode 100644
index 0000000000..298da70eff
--- /dev/null
+++ b/files/fr/learn/server-side/django/models/index.html
@@ -0,0 +1,468 @@
+---
+title: 'Django didactique Section 3: Utilisation des modèles de données'
+slug: Learn/Server-side/Django/Models
+translation_of: Learn/Server-side/Django/Models
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Ce troisième article est consacré aux modèles de données pour les sites web générés avec Django. Après une définition et une présentation de la notion de modèle de données, il explique comment les déclarer, choisir le type de champs et quelques méthodes d'accès au modèle de données via Django.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pré-requis:</th>
+ <td>
+ <p><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Django didactique Section 2: Créer le squelette du site web.</a></p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Objectif:</th>
+ <td>
+ <p>Concevoir et créer vos propres modèles de données et choisir les attributs idoines.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Survol">Survol</h2>
+
+<p>Les applications web Django donnent accès aux données enregistrées dans une base à l'aide d'une classe d'objets <em>models</em>. Une classe d'objet héritée de <em>models</em> définit une <em>structure de données</em> ainsi que le type, la taille et la nature des champs de cette structure de données. Cela inclut aussi la valeur par défaut, les options ou listes d'option, les informations pour l'aide en ligne ou la définition des étiquettes des champs des formulaires. La définition du modèle de données est une abstraction indépendante du gestionnaire de base de données associé ; une fois choisi le gestionnaire est sollicité par le biais des objets Python/Django et vous n'interagissez pas directement avec lui. Votre rôle est alors de décrire le modèle de données par le biais d'objets appropriés et Django prend en charge les communications avec la base de données.</p>
+
+<p>Ce chapitre vous montre sur la base du <a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">site web d'une bibliothèque locale</a> comment concevoir, créer et manipuler les données par l'intermédiaire du modèle de données.</p>
+
+<h2 id="Concevoir_le_modèle_de_données_de_la_bibliothèque_locale">Concevoir le modèle de données de la bibliothèque locale</h2>
+
+<p>Une étape préliminaire à la phase de développement est de réfléchir aux données (et donc aux structures de données) nécessaires et utiles pour le projet et aux relations entre-elles.</p>
+
+<p>Nous aurons besoin de conserver des données sur des livres (titre, résumé, auteur, version original, catégorie, ISBN), le nombre disponible en stock (donc un identifiant unique par livre et le statut du prêt). Nous aurons probablement besoin d'enregistrer des informations sur les auteurs qui ne seront pas uniquement le nom ainsi que gérer l'homonymie. Enfin, il sera nécessaire d'ordonner et de trier ces informations par titre, auteur, langue ou catégorie.</p>
+
+<p>En conception, il est judicieux et recommandé de décrire chacun des objets séparément. Dans le cas présent, il nous faudra un objet pour les livres, les copies des livres et les auteurs.</p>
+
+<p>Vous pourriez aussi utiliser les modèles pour définir des listes d'options (comme une liste déroulante pour un choix), plutôt que d'avoir à implémenter avec le code du site web ces choix. C'est d'ailleurs une recommandation à considérer si les options ne sont pas parfaitement connues à l'avance. Ce sera typiquement le cas des catégories de livres (science fiction, poésie, littérature étrangère, etc.) ou des langues des version originales (Français, Anglais, Espagnol, etc.).</p>
+
+<p>Le choix du modèle étant posé, nous avons à considérer les relations entre les objets. Django permet d'établir trois types de relation : les relations un à un qui mettent en relation un et un seul objet avec un autre (<code>OneToOneField</code>), les relations un à n qui partage l'appartenance d'un objet à avec d'autres (<code>ForeignKey</code>) et les relations n à n qui associent des groupes d'objets entre-eux (<code>ManyToManyField</code>).</p>
+
+<p>Avec ces éléments présents à l'esprit, le diagramme  de classes UML ci-dessous décrit les objets de la bibliothèque.</p>
+
+<p><img alt="LocalLibrary Model UML" src="https://mdn.mozillademos.org/files/16479/local_library_model_uml.png" style="height: 660px; width: 977px;"></p>
+
+<p>Le modèle ainsi créé, décrit l'objet livre - <em>Book</em> - avec une description générique d'un livre, la copie d'un livre - <em>BookInstance</em> - avec l'état d'un copie physique d'un livre et de sa disponibilité, et l'objet auteur - <em>Author</em>. Les genres des collections pouvant varier, il est plus élégant de concevoir une classe d'objets dédiée comme pour les langues. Considérant que le statut de prêt ne changera pas, il est décidé que le traiter dans le code - <code>BookInstance:status</code> sera géré dans le code Django <code>LOAN_STATUS</code>. Dans le diagramme de classe, les caractéristiques de chacun des attributs et méthodes sont précisées pour plus de clarté du travail à réaliser.</p>
+
+<p>Le diagramme met aussi en évidence les relations entre les objets et la cardinalité des relations. La cardinalité est représentée par les nombres entre crochet avec, si nécessaire, un minimum et un maximum. Par exemple, un ouvrage a, au moins un genre ([1..*]) alors qu'un genre peut ne pas référencer un livre ([0..*]) ce qui se traduira en définition des objets dans models.py.</p>
+
+<div class="note">
+<p><strong>Note</strong>: La section ci-dessous est une introduction générale à la modélisation des objets pour les modèles de données dans Django. Gardez à l'esprit la bibliothèque locale et imaginez comment devraient être décrits les objets pour cette bibliothèque.</p>
+</div>
+
+<h2 id="Introduction_au_modèle_de_données">Introduction au modèle de données</h2>
+
+<p>Cette section fournit une rapide introduction à la définition des objets de conception du modèle de données. </p>
+
+<h3 id="Spécification">Spécification</h3>
+
+<p>Les objets sont <strong>toujours</strong> définis dans le fichier <strong>models.py</strong> de chaque application. Ils sont conçus comme sous-classe de <code>django.db.models.Model</code>, et sont caractérisés par des attributs ou champs, des méthodes et des métadonnées. L'extrait ci-dessous définit donc la classe <code>MyModelName</code>:</p>
+
+<pre class="notranslate">from django.db import models
+
+class MyModelName(models.Model):
+    """A typical class defining a model, derived from the Model class."""
+
+  # Fields
+    my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
+  ...
+
+  # Metadata
+ class Meta:
+  ordering = ['-my_field_name']
+
+  # Methods
+    def get_absolute_url(self):
+        """Returns the url to access a particular instance of MyModelName."""
+        return reverse('model-detail-view', args=[str(self.id)])
+
+    def __str__(self):
+        """String for representing the MyModelName object (in Admin site etc.)."""
+        return self.my_field_name</pre>
+
+<p>Détaillons ce qu'il en retourne :</p>
+
+<h4 id="Champs_ou_attributs">Champs ou attributs</h4>
+
+<p>Chaque objet peut contenir autant d'attributs que de besoin et de quelque type qu'il soit. Chaque attribut correspondra à une colonne - <em>ou champ</em> - dans une table de la base de données. Chaque enregistrement, ou ligne dans la table, correspondra à une instance de la classe d'objet et chaque champ sera évalué. Un champ est de la forme :</p>
+
+<pre class="brush: js notranslate">my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')</pre>
+
+<p>Dans l'exemple ci-dessus, le champs est une chaîne de caractères — de type <code>models.CharField</code> —  dont le nom est <code>my_field_name</code>. Les champs ont des types pré-définis représentés par une classe d'objet Django qui va permettre de caractériser une champ du modèle de données. Cela permet aussi de valider les données qui seront fournies via les formulaires du site web décrits avec le langage HTML. Les classes caractérisant les type de champs peuvent accepter des paramètres pour préciser les contraintes appliquées à ce champ. Dans cet exemple, deux arguments sont indiqués :</p>
+
+<ul>
+ <li><code>max_length=20</code> — Défini que ce champs fait au plus 20 caractères.</li>
+ <li><code>help_text='Enter field documentation'</code> — attribue un label par défaut qui sera affiché dans la page web par le navigateur.</li>
+</ul>
+
+<p>Le nom du champs sera utilisé dans les requêtes et les gabarits. Ces champs peuvent avoir une étiquette à l'aide d'un argument de définition (<code>verbose_name</code>) ou ils seront déduits à l'aide d'un changement de casse ou le remplacement des espaces par des soulignés (comme par exemple <code>my_field_name</code> serait l'étiquette par défaut du champs <em>My field name</em>).</p>
+
+<p>L'ordre dans lequel est défini un attribut de la classe d'objet va définir la position de la colonne dans le modèle physique de la base de données ce qui affectera, même la présentation est modifiable, la présentation par défaut des champs dans les formulaires ; c'est notamment le cas pour la partie administration du site web.</p>
+
+<h5 id="Les_arguments_courants_des_champs">Les arguments courants des champs</h5>
+
+<p>Vous trouverez ci-dessous les arguments les plus utilisés dans la définition des champs :</p>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#help-text">help_text</a>: définit l'étiquette du champ qui apparaîtra par défaut dans les formulaires HTML (notamment sur la section Administration du site).</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#verbose-name">verbose_name</a>: définit un nom vernaculaire du champs technique qui sera alors utilisé comme étiquette. Si ce nom n'est pas défini alors Django va le déduire du nom technique.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#default">default</a>: définit la valeur par défaut du champs. Ce peut être une valeur alphanumérique mais aussi un objet créé appelable qui sera sollicité à chaque création d'un objet appelant.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#null">null</a>: définit si le champs peut ne pas être remplit dans la base de données ; s'il est à vrai ( <code>True</code>), alors Django considère qu'une valeur <code>NULL</code> peut être correctement enregistrée - pour une chaîne de caractère de type <code>CharField</code> il s'agira d'une chaîne vide. La valeur par défaut de cet attribut est à faux (<code>False</code>) c'est-à-dire qu'il est nécessaire d'avoir une donnée non vide à l'enregistrement par défaut.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#blank">blank</a>: définit si un champ d'un formulaire peut ne pas être saisi ; s'il est à vrai (<code>True</code>), le champs peut être laissé vide dans le formulaire. Par défaut ce paramètre est à faux (<code>False</code>), ce qui traduit que Django exigera une saisie d'une information dans le champs. Ce paramètre est utilisé en complément de la valeur <code>null=True</code>, car si vous acceptez une valeur vide dans la base de données, il est inutile d'en obliger la saisie dans un formulaire web.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#choices">choices</a>: définit une liste de choix possible pour un champs et sera traduit par un composant d'interface de type liste de choix.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#primary-key">primary_key</a>: S'il est à <code>True</code>, il définit le champ comme une clé primaire, c'est-à-dire qui permet d'identifier de manière unique un enregistrement dans la table de la base de données, pour le modèle de données. S'il n'y a pas de clé primaire, Django en affectera une d'office.</li>
+</ul>
+
+<p>L'ensemble <a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#field-options">des options de champs</a> peut être consulté sur le site Django.</p>
+
+<h5 id="Les_types_courrants_de_champs">Les types courrants de champs</h5>
+
+<p>Vous trouverez ci-dessous les arguments les principaux type de champs :</p>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#django.db.models.CharField">CharField</a> caractérise un champ de type chaîne de caractères de taille maximale fixe. Ce champ nécessite l'option obligatoire <code>max_length</code> pour définir la taille maximale de la chaîne de caractère.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#django.db.models.TextField">TextField</a> caractérise un champs texte (de longeur non définit dans la base de données). Si l'option <code>max_length</code> est utilisé, celui-ci précisera la taille du champs texte des formulaires web mais n'aura pas d'impact dans la définition du champs en base de données.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#django.db.models.IntegerField" title="django.db.models.IntegerField">IntegerField</a> caractérise un champs de type nombre entier.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#datefield">DateField</a> et <a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#datetimefield">DateTimeField</a> sont des type utilisées pour caractériser une date et une heure comme les objets <code>datetime.date</code> et <code>datetime.datetime</code> en Python. Les options (incompatibles ensemble) les plus courantes pour ces champs sont l'enregistrement au moment de la sauvegarde (<code>auto_now=True</code>), l'enregistrement à la création de l'objet (<code>auto_now_add</code>) et une valeur par défaut (<code>default)</code> qui pourra être changée par l'utilisateur.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#emailfield">EmailField</a> est le type dédié à la gestion des courriers électroniques.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#filefield">FileField</a> et <a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#imagefield">ImageField</a> sont deux type utilisés pour permettre de télécharger des fichiers ou, plus spécifiquement des images. Les options de ces champs définissent où et comment ces fichiers seront enregistrés et conservés.</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/ref/models/fields/#autofield">AutoField</a> est un type particulier de nombre entier (<code>IntegerField</code>) qui est incrémenté automatiquement. S'il n'y a pas de clé primaire automatiquement déclarée alors un champs de ce type est automatiquement déclaré dans dans le modèle de données.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#foreignkey">ForeignKey</a> est utilisé pour définir une relation un à plusieurs entre deux classe d'objet (ou deux enregistrements dans deux tables de la base de données). Plusieurs objets contenant la définition d'un champ de type <code>ForeignKey</code> peuvent faire référence à une seule et même clé ; tel est le sens de la relation un à plusieurs, ce n'est donc pas la clé étrangère qui porte la cardinalité de la relation.</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#manytomanyfield">ManyToManyField</a> est utilisé pour définir une relation n à n (e.g. un nom commun a plusieurs sens et un sens peut être décrit par plusieurs noms communs). Dans notre application bibliothèque, nous utiliserons ce type de champs d'une manière proche de la clé étrangère. Cependant, cette relation peut être utilisée pour exprimer des relations plus compliquées entre des groupes. Ce champ requiert l'option <code>on_delete</code> pour préciser le comportement de l'attribut quand l'enregistrement est supprimé (e.g. la valeur de l'attribut <code>models.SET_NULL</code> peut être affecté à <code>NULL</code>).</li>
+</ul>
+
+<p>L'ensemble <a href="https://docs.djangoproject.com/fr/2.2/ref/models/fields/#field-types">des types de champs</a> peut être consulté sur le site Django.</p>
+
+<h4 id="Métadonnées">Métadonnées</h4>
+
+<p>Vous avez la capacité de déclarer des métadonnées à l'aide de la classe  <code>class Meta</code>, comme précisé ci-dessous :</p>
+
+<pre class="brush: python notranslate">class Meta:
+ ordering = ['-my_field_name']
+</pre>
+
+<p>L'une des fonctionnalités les plus utiles disponible à l'aide des métadonnées est de définir et contrôler le classement des enregistrement. Vous l'obtenez en précisant la liste des champs dans l'attribut <code>ordering</code> comme indiqué ci-dessous. Le classement est fonction du type de l'attribut (une chaîne de caractère a un classement alphabétique alors qu'une date a un classement chronologique). Si vous préfixez le nom du champs du signe <em>moins</em> (-) alors le classement sera naturellement inversé.</p>
+
+<p>Voici un exemple de classe de livre par titre et dates de parution :</p>
+
+<pre class="brush: python notranslate">ordering = ['title', '-pubdate']</pre>
+
+<p>Les livres sont présenté dans l'ordre alphabétique de leur titre, puis dans l'ordre chronologique du plus récent au plus ancien.</p>
+
+<p>Un autre attribut très utile est celui d'un nom vernaculaire pour la classe, <code>verbose_name</code>  peut être au singulier et au pluriel :</p>
+
+<pre class="brush: python notranslate">verbose_name = 'BetterName'</pre>
+
+<p>D'autres attributs vous permettent de compléter des droits d'accès à ceux appliqués par défaut, des classement s'appuyant sur le comportement d'autres champs, ou de définir une classe abstraite (c'est-à-dire qui n'aura pas de transcription dans une table et des enregistrement, mais servira de support à d'autres classes partageant des éléments communs).</p>
+
+<p>D'autres éléments sont aussi disponible pour contrôler le comportement d'une base de données, mais sont principalement utilisé pour appliquer le modèle ORM sur une base de données déjà existante.</p>
+
+<p>L'ensemble <a href="https://docs.djangoproject.com/fr/2.2/ref/models/options/">des métadonnées de classe</a> peut être consulté sur le site Django.</p>
+
+<h4 id="Méthodes">Méthodes</h4>
+
+<p>Comme tout objet Python, une classe héritée de <code>model</code> peut utiliser des méthodes.</p>
+
+<p><strong>A minima, chaque modèle de données - c'est-à-dire une classe héritée de la classe model du module django.db - vous devez définir la méthode <code>__str__()</code> pour permettre d'afficher un élément compréhensible qui représentera l'instance de la classe.</strong> Cette méthode est aussi utilisée au niveau du site d'administration pour afficher les instances de la classe administrée. La plupart du temps, cette méthode retourne un titre ou nom associé à aux objets de la classe.</p>
+
+<pre class="brush: python notranslate">def __str__(self):
+  return self.field_name</pre>
+
+<p>Une seconde méthode très utilisée dans le cadriciel Django est <code>get_absolute_url()</code>. Elle permet de fournir un URL pour afficher dans le site web le contenu de de chacun des enregistrements associés au modèle de données décrit. Si vous utilisez cette méthode, Django ajoutera un bouton pour permet de visualiser le détail des enregistrements. Classiquement, une méthode <code>get_absolute_url()</code> est de la forme :</p>
+
+<pre class="brush: python notranslate">def get_absolute_url(self):
+ """Returns the url to access a particular instance of the model."""
+ return reverse('model-detail-view', args=[str(self.id)])
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: En supposant que vous allez utiliser des URLs du type <code>/myapplication/mymodelname/2</code> pour afficher individuellement les données des enregistrements de la table associée à votre modèle de données (où "2" est l'<code>id</code>entifiant d'un enregistrement donné), vous devrez créer un routage d'URL pour vous permettre de transmettre l'id à une vue détaillée de l'enregistrement (model detail view dans le cadriciel Django). Cette vue détaillée réalisera l'affichage de l'enregistrement. La fonction <code>reverse()</code> a pour objectif d'écrire l'URL dans un format cohérent avec le traitement des URL par les navigateurs.</p>
+
+<p>Bien entendu, cela requiert d'écrire le routage de l'URL, la vue et le gabarit...</p>
+</div>
+
+<p>Vous pouvez aussi définir toute les méthodes dont vous aurez besoin pour manipuler à travers les objets du modèle de données les enregistrements de la base de données.</p>
+
+<h3 id="Administration_des_données">Administration des données</h3>
+
+<p>A partir du moment où vous avez créé votre modèle de données, vous pouvez manipuler les instances pour créer, mettre à jour ou supprimer les enregistrements en base de données. Vous pouvez aussi faire des requêtes pour obtenir tout ou parti des enregistrements de la base. L'objet de cette section est d'évoquer la manière de manipuler ces données et cela revu progressivement dans les avancées de l'application Bibliothèque.</p>
+
+<h4 id="Créer_et_modifier_des_enregistrements">Créer et modifier des enregistrements</h4>
+
+<p>Pour créer un enregistrement, il suffit de définir une instance de la classe d'objet et de la sauvegarder avec la méthode <code>save()</code>.</p>
+
+<pre class="brush: python notranslate"># Créer un nouvel enregistrement en utilisant la méthode d'instanciation.
+record = MyModelName(my_field_name="Instance #1")
+
+# Sauvegarde de l'enregistrement en base de données.
+record.save()
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Si aucun champs n'a été défini comme une clé primaire (option <code>primary_key</code>), un champs nommé <code>id</code> ou <code>pk</code> sera affecté au modèle et sera incrémenté automatiquement. Vous pouvez requêter cet enregistrement à l'aide de ce champ ; le premier enregistrement aura habituellement la valeur entière 1.</p>
+</div>
+
+<p>Les champs de l'enregistrement sont accessibles à l'aide des attributs de la classe d'objet. En utilisant la syntaxe pointée, vous pouvez modifier les valeurs des champs de l'enregistrement. Vous devez utiliser la méthode <code>save()</code> pour enregistrer en base de données les modifications.</p>
+
+<pre class="brush: python notranslate"># Accès au valeur des champs par le biais des attributs de classe Python.
+print(record.id) # devrez retourner la valeur 1 pour le premier en enregistrement.
+print(record.my_field_name) # devrez afficher 'Instance #1'
+
+# Changer la valeur d'un champs et le sauvegarder en base avec la méthoide save().
+record.my_field_name = "New Instance Name"
+record.save()</pre>
+
+<h4 id="Rechercher_des_enregistrements">Rechercher des enregistrements</h4>
+
+<p>La classe de base <code>objects</code> permet de faire des recherches d'enregistrement qui correspondront aux critères de recherche souhaités.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Nous utiliserons dans les explications le modèle de données d'un livre (<code>Book</code>)avec des titres (<code>title</code>) et des genres littéraires (<code>genre</code>), car expliquer la manière de rechercher sur un modèle théorique n'est pas très pédagogique.</p>
+</div>
+
+<p>Vous pouvez obtenir tous les enregistrements d'un modèle de données sous la forme d'un jeu de données ou <code>QuerySet</code>, en utilisant <code>objects.all()</code>. Un <code>QuerySet</code> est un objet itérable, c'est-à-dire jeu de données contenant des objets que l'on peut parcourir.</p>
+
+<pre class="brush: python notranslate">all_books = Book.objects.all()
+</pre>
+
+<p>Un filtre Django ou <code>filter()</code> est une méthode qui permet de sélectionner un jeu de données répondant à des critères (texte ou numérique) de sélection. Par exemple, nus filtrons les livres dont le titre contient le mot "wild", puis nous dénombrons le jeu de données.</p>
+
+<pre class="brush: python notranslate">wild_books = Book.objects.filter(title__contains='wild')
+number_wild_books = wild_books.count()
+</pre>
+
+<p>Les arguments passés en option sont le champs et la nature du contrôle à effectuer. On utilise le format : <code>field_name__match_type</code> : dans l'exemple ci-dessus, le double sous-ligné marque la séparation entre le champ <code>title</code> et le type de contrôle <code>contains</code> ; concrètement, le filtre est appliqué sur le champ <code>title</code> contenant le mot <code>wild</code> en respectant la casse. Il existe d'autres options de contrôle : <code>icontains</code> (sans respect de la casse), <code>iexact</code> (le chmaps correspond exactement à la valeur donnée sans respect de la casse), <code>exact</code> (idem en respectant la casse) et <code>in</code>, <code>gt</code> (plus grand que), <code>startswith</code>(commence par), etc. La liste complète est <a href="https://docs.djangoproject.com/fr/2.2/ref/models/querysets/#field-lookups">consultatble sur la documentation de Django</a>.</p>
+
+<p>Le marqueur "double souligné" permet de construire une chaîne de navigation à travers les objets lorsque le champ considéré est une clé étrangère (<code>ForeignKey</code>). C'est systématiquement le cas lorsque l'on doit filtrer sur une propriété d'un attribut dans une relation un-à-un. Dans ce cas (exemple ci-dessous), vous identifiez l'attribut de la clé étrangère par le biais d'un "double souligné" qui indique le champs à filter. L'exemple ci-dessous indique que vous filtrez les livres selon le nom (<code>name</code>) du genre (<code>genre</code>) du livre.</p>
+
+<pre class="brush: python notranslate"># Le criètre s'appliquera sur les genres contenant 'fiction' i.e. : Fiction, Science fiction, non-fiction etc.
+books_containing_genre = Book.objects.filter(genre<strong>__</strong>name<strong>__</strong>icontains='fiction')
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Vous pouvez construire une chemin pour naviguer dans autant de niveaux de relation (<code>ForeignKey</code>/<code>ManyToManyField</code>) que vous en avez besoin en concaténant des noms de champs à l'aide  (__) . Si par exemple vous souhaitez trouver un livre (<code>Book</code>) qui possède différents type (<code>type</code>) de couvertures (<code>cover</code>) identifiées par des noms (<code>name</code>) alors le chemin sera du type : <code>type__cover__name__exact='hard'.</code></p>
+</div>
+
+<p>La mise en oeuvre des requêtes est très riches en fonction des modèles et des relations, de sous-ensemble de données, etc. Pour une informations détaillées, vous devez consulter <a href="https://docs.djangoproject.com/fr/2.2/topics/db/queries/">les requêtes</a> sur le site de référence de Django.</p>
+
+<h2 id="Définition_du_modèle_de_données_de_lapplication_LocalLibrary">Définition du modèle de données de l'application LocalLibrary</h2>
+
+<p>Cette section est consacrée au démarrage de la définition de l'application LocalLibrary qui permet de gérer une petite bibliothèque locale. Ouvrez le fichier <em>models.py </em>présent dans le répertoire<em> /locallibrary/catalog/</em>. Le code par défaut est déjà en place au début du document et permet d'importer les éléments du module models de django.</p>
+
+<pre class="brush: python notranslate">from django.db import models
+
+# Create your models here.</pre>
+
+<h3 id="Lobjet_Genre">L'objet Genre</h3>
+
+<p>Cet objet est utilisé pour décrire et enregistrer le genre littéraire des livres — par exemple une fiction, une polard ou un roman. Comme cela a été évoqué précédemment, nous créons un modèle de données plutôt que de gérer cela à l'aide de texte libre ou de codage en dur. Copiez le texte ci-dessous à la fin du fichier <em>models.py</em>.</p>
+
+<pre class="brush: python notranslate">class Genre(models.Model):
+    """Cet objet représente une catégorie ou un genre littéraire."""
+    name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
+
+    def __str__(self):
+        """Cette fonction est obligatoirement requise par Django.
+ Elle retourne une chaîne de caractère pour identifier l'instance de la classe d'objet."""
+        return self.name</pre>
+
+<p>L'objet, en relation avec la base de données, possède un seul attribut (<code>name</code>) de type chaîne de caractères (<code>CharField</code>), qui sera utilisé pour décrire le genre d'un livre (limité à 200 caractères). Une option (<code>help_text)</code> permet d'utiliser une étiquettes d'aide dans les pages et formulaires du site web. La méthode <code>__str__()</code>, qui retourne simplement le nom du genre littéraire de chaque enregistrement. Puisque qu'il n'y a pas de nom vernaculaire (<code>verbose_name</code>), le champ sera simplement nommé <code>Name</code> dans les formulaires.</p>
+
+<h3 id="Lobjet_Book">L'objet Book</h3>
+
+<p>Comme précédemment, vous pouvez copier le descriptif de l'objet Book à la fin du fichier models.py. Cet objet représente un livre dans sa description et non une copie en rayon disponible au prêt. Par conséquent, l'objet contient un titre et son identifiant international (isbn dont on notera l'étiquette en majuscule pour ne pas avoir "Isbn" à la place) sous forme de chaînes de caractère. De plus, l'objet contient un résumé sous forme d'une chaîne de caractère de longueur non explicite pour traiter de résumés plus ou moins long.</p>
+
+<pre class="brush: python notranslate">from django.urls import reverse # Cette fonction est utilisée pour formater les URL
+
+class Book(models.Model):
+    """Cet objet représente un livre (mais ne traite pas les copies présentes en rayon)."""
+    title = models.CharField(max_length=200)
+
+ # La clé étrangère (ForeignKey) est utilisée car elle représente correcte le modèle de relation en livre et son auteur :
+ # Un livre a un seul auteur, mais un auteur a écrit plusieurs livres.
+ # Le type de l'objet Author est déclré comme une chaîne de caractère car
+ # la classe d'objet Author n'a pas encore été déclarée dans le fichier
+    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
+
+    summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
+    isbn = models.CharField('ISBN', max_length=13, help_text='13 Character &lt;a href="https://www.isbn-international.org/content/what-isbn"&gt;ISBN number&lt;/a&gt;')
+
+    # Le type ManyToManyField décrit correctement le modèle de relation en un livre et un genre.
+ # un livre peut avoir plusieurs genres littéraire et réciproquement.
+    # Comme la classe d'objets Genre a été définit précédemment, nous pouvons manipuler l'objet.
+ genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
+
+    def __str__(self):
+        """Fonction requise par Django pour manipuler les objets Book dans la base de données."""
+        return self.title
+
+    def get_absolute_url(self):
+        """Cette fonction est requise pas Django, lorsque vous souhaitez détailler le contenu d'un objet."""
+        return reverse('book-detail', args=[str(self.id)])
+</pre>
+
+<p>Le genre littéraire est une relation n à n (<code>ManyToManyField</code>)car un livre peut avoir plusieurs genre et inversement. Bien que des livres soient écrits à plusieurs, dans le modèle de données présent un livre n'aura qu'un et un seul auteur. Un auteur est donc vu comme une clé étrangère <code>(ForeignKey</code>) de telle sorte qu'un livre n'a qu'un seul auteur et une auteur peut avoir écrit plusieurs livres.</p>
+
+<p>La modélisation des relations entre les objets, c'est le cas pour les deux champs décrits à l'instant, nécessite de manipuler les classes d'objet par leur nom de classe. Vous devez déclarer l'objet par son de classe dans la déclaration de la relation entre les objets, si celui-ci a déjà été déclaré vous pouvez l'utiliser comme un nom d'objet - à l'identique d'une variable Python - ou comme une chaîne de caractère si l'objet n'a pas déjà fait l'objet d'un déclaration. les autres paramètres dans la déclaration des relations permettent de spécifier les comportement des attributs : l'option <code>null</code> positionné à <code>True</code> permet d'avoir un contenu vide en base de données, la second option<code> on_delete=models.SET_NULL</code> qualifie le fonctionnement de cet attribut si l'objet est supprimé en base de données, en l'occurence il peut être positionné à vide en base de données.</p>
+
+<p>Deux méthodes sont déclarées pour cet objet. La méthode <code>__str__()</code> obligatoirement requise par Django pour manipuler les instances d'objet et les enregistrements associés en base. La seconde méthode, <code>get_absolute_url()</code>, retourne une URL formatée qui peut être utilisée par le cadriciel pour délivrer le détail de chaque instance d'objet de la classe. Le routage d'URL sera associé au nom  <code>book-detail</code>, et nous aurons à définir une vue et un gabarit.</p>
+
+<h3 id="Lobjet_BookInstance">L'objet BookInstance</h3>
+
+<p>Occupons nous maintenant de l'objet <code>BookInstance</code>. Comme précédemment, copiez le contenu décrivant l'objet BookInstance ci-dessous dans votre fichier <em>models.py</em>. La classe d'objets décrit une copie d'un ouvrage qu'un individu peut physiquement emprunter. Elle prend en compte les éléments d'information qui permettent de l'identifier individuellement et de connaître son statut à chaque instant ainsi que la date de retour du prêt.</p>
+
+<p>Les attributs et méthodes vont vous paraître familiers. On utilise :</p>
+
+<ul>
+ <li>une clè étrangère (<code>ForeignKey</code>) pour modéliser la relation avec le livre (un livre disposant de plusieurs copies).</li>
+ <li>Une chaîne de caractères (<code>CharField)</code> pour enregistrer les mentions légales (imprint) du livre.</li>
+</ul>
+
+<pre class="brush: python notranslate">import uuid # Ce module est nécessaire à la gestion des identifiants unique (RFC 4122) pour les copies des livres
+
+class BookInstance(models.Model):
+ """Cet objet permet de modéliser les copies d'un ouvrage (i.e. qui peut être emprunté)."""
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
+ book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
+ imprint = models.CharField(max_length=200)
+ due_back = models.DateField(null=True, blank=True)
+
+ LOAN_STATUS = (
+ ('m', 'Maintenance'),
+ ('o', 'On loan'),
+ ('a', 'Available'),
+ ('r', 'Reserved'),
+ )
+
+ status = models.CharField(
+ max_length=1,
+ choices=LOAN_STATUS,
+ blank=True,
+ default='m',
+ help_text='Book availability',
+ )
+
+ class Meta:
+ ordering = ['due_back']
+
+ def __str__(self):
+ """Fonction requise par Django pour manipuler les objets Book dans la base de données."""
+ return f'{self.id} ({self.book.title})'</pre>
+
+<p>De nouveaux types de champs sont utilisés :</p>
+
+<ul>
+ <li>Le type <code>UUIDField</code> est utilisé pour traiter d'un identifiant unique de livre comme clé primaire. Ce type de champ permet de générer un identifiant unique pour enregistrer et suivre chacune des copies de chacun des livres.</li>
+ <li>Le type <code>DateField</code> est utilisé pour enregistrer la date de retour d'un prêt. Ce champ peut-être vide pour gérer le cas des livres dans les rayonnages c'est-à-dire disponibles pour un prêt. Il est fait appel à la classe <code>Meta</code> pour permettre de classer les requêtes sur les objet par date de retr</li>
+ <li>our.</li>
+ <li>Le champ <code>status</code> est un type connu (<code>CharField</code>) qui définit une liste de choix. Les choix sont définis dans la description de l'objet par l'usage de tuples (une paire clé-valeur) et transmis en option dans la déclaration du champs. Alors que l'utilisateur manipulera les valeurs, les clés seront enregistrées dans la base de données. Enfin, la valeur par défaut est la Maintenance car lorsqu'un ouvrage est créé il n'est pas immédiatement disponible au prêt et n'est pas directement positionné en rayon.</li>
+</ul>
+
+<p>La méthode <code>__str__()</code> obligatoirement requise par Django pour manipuler les instances d'objet et les enregistrements associés en base. Elle offre cependant la particularité d'associer l'identifiant unique et le titre du livre qui lui est associé.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Un aspect de Python:</p>
+
+<ul>
+ <li>Si vous démarrez avec une version postérieure à la version 3.6, vous pouvez utiliser le formatage des chaînes de caractère avec la fonction f-strings : <code>f'{self.id} ({self.book.title})'</code>.</li>
+ <li>Dans les versions précédente ce formatage était réalisé de manière différente utilisant la fonction de formatage format : <code>'{0} ({1})'.format(self.id,self.book.title)</code>).</li>
+</ul>
+</div>
+
+<h3 id="Lobjet_Author">L'objet Author</h3>
+
+<p>Terminons en copiant la description de l'objet <code>Author</code> à la fin du fichier <strong>models.py</strong>.</p>
+
+<pre class="brush: python notranslate">class Author(models.Model):
+ """Model representing an author."""
+ first_name = models.CharField(max_length=100)
+ last_name = models.CharField(max_length=100)
+ date_of_birth = models.DateField(null=True, blank=True)
+ date_of_death = models.DateField('Died', null=True, blank=True)
+
+ class Meta:
+ ordering = ['last_name', 'first_name']
+
+ def get_absolute_url(self):
+ """Returns the url to access a particular author instance."""
+ return reverse('author-detail', args=[str(self.id)])
+
+ def __str__(self):
+ """String for representing the Model object."""
+ return f'{self.last_name}, {self.first_name}'
+</pre>
+
+<p>Désormais les notions manipulées pour définir cet objet vous sont connues. L'objet réprésente un auteur par ses nom et prénoms ainsi que par ses dates de naissance et de décès (celles-ci n'étant pas obligatoires). Deux méthodes permettent l'une d'accéder à l'objet de manière compréhensible (<code>__str__()</code>) en retournant les nom et prénom de l'auteur dans cet ordre, et, l'autre (<code>get_absolute_url()</code>) permettra de publier les informations propres à chaque auteur.</p>
+
+<h2 id="Appliquer_les_modifications_en_base">Appliquer les modifications en base</h2>
+
+<p>Les objets sont tous décrits dans le fichier dédié à la modélisation. Pour qu'elles soient effectives, il est nécessaire d'exécuter les deux commandes python qui gèrent les migrations de la base de données.</p>
+
+<pre class="notranslate"><code>python3 manage.py makemigrations
+python3 manage.py migrate</code></pre>
+
+<h2 id="Défi_—_Introduire_les_langues">Défi — Introduire les langues</h2>
+
+<p>Faisons l'hypothèse qu'un donateur lègue à la bibliothèque des livres dont certains sont écrits dans des langues étrangères comme le Farsi (langue majoritaire en Iran). Le défi consiste donc à modéliser puis utiliser la meilleure représentation possible de ce concept pour la bibliothèque.</p>
+
+<p>Gardez en tête que :</p>
+
+<ul>
+ <li>Une langue peut-être associée à plusieurs objets dont au moins <code>Book</code>, <code>BookInstance</code></li>
+ <li>Plusieurs types peuvent être utiliser pour modéliser une langue un objet, un champs, ou explicitement dans le code à l'aide d'une liste de choix</li>
+</ul>
+
+<p>Après avoir fait vos choix, modéliser le et ajouter les champs utiles. Vous pouvez ensuite voir sur <a href="https://github.com/mdn/django-locallibrary-tutorial/blob/master/catalog/models.py">Github nous l'avons fait</a>.</p>
+
+<p>Une dernière chose... n'oubliez pas d'appliquer les modifications en base de données</p>
+
+<pre class="notranslate"><code>python3 manage.py makemigrations</code><code>
+python3 manage.py migrate</code></pre>
+
+<ul>
+</ul>
+
+<ul>
+</ul>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Cet article est consacré à la création des objets et leur lien en base de données ainsi qu'à leur gestion. Il s'appuie sur l'exemple de la bibliothèque locale pour lequel nous décrivons le design du modèle relationnel et la manière de l'implementer avec une description d'objet Python conforme au standard du cadriciel Django.</p>
+
+<p>A ce stade, il est prématuré de créer le site web, nous allons simplement utiliser le site d'administration qui permet d'ajouter et de manipuler des données. Nous afficherons ces informations ensuite en créant des vues et de gabarits.</p>
+
+<h2 id="A_voir_aussi">A voir aussi</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/intro/tutorial02/">Ecriture de votre première application Django, Deuxième partie</a> (Documentation Django)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/topics/db/queries/">Création de requêtes</a> (Documentation Django)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/models/querysets/">Référence de l'API QuerySet</a> (Documentation Django)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}</p>
+
+<h2 id="Dans_ce_module">Dans ce module</h2>
+
+<ul>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Didactique: Site web "Bibliothèque locale"</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Django didactique Section 2: Créer le squelette du site web</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Models">Django didactique Section 3: Utilisation des modèles de données</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Admin_site">Django didactique Section 4 : Site d'administration de Django</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Home_page">Django didactique Section 5: Créer la page d'accueil</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/fr/learn/server-side/django/skeleton_website/index.html b/files/fr/learn/server-side/django/skeleton_website/index.html
new file mode 100644
index 0000000000..787db15aec
--- /dev/null
+++ b/files/fr/learn/server-side/django/skeleton_website/index.html
@@ -0,0 +1,403 @@
+---
+title: 'Django didactique Section 2: Créer le squelette du site web'
+slug: Learn/Server-side/Django/skeleton_website
+tags:
+ - Apprentissage
+ - Article
+ - Didactitiel
+ - Débutant
+ - Guide
+ - Intro
+ - Programmation
+ - Tutoriel
+ - django
+translation_of: Learn/Server-side/Django/skeleton_website
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Ce second article de la série <a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">didactique Django</a> va décrire comment créer le squelette du site web du projet. Ensuite, vous pourrez paramètrer et développer les composants spécifiques comme les modèles de données, les vues, les gabarits, les formulaires...</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis:</th>
+ <td><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Set up a Django development environment</a>. Avoir pris connaissance de <a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">l'article précédent</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objectifs:</th>
+ <td>Être capable d'utiliser les outils de Django pour initier un nouveau projet.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Vue_densemble">Vue d'ensemble</h2>
+
+<p>Cet article décrit comment créer le squelette du site web du projet. Ensuite, vous pourrez paramètrer et développer les composants spcifiques comme les modèles de données, vues, formulaires... qui chacun seront vus plus en details dans les articles suivants.</p>
+
+<p>La création est aisée:</p>
+
+<ol>
+ <li><span style="line-height: 1.5;">Utilisez la commande </span><code style="font-style: normal; font-weight: normal; line-height: 1.5;">django-admin</code><span style="line-height: 1.5;"> pour créer le dossier du projet ainsi que les sous-dossiers et fichiers de base ainsi que le script de gestion du projet (</span><strong style="line-height: 1.5;">manage.py</strong><span style="line-height: 1.5;">).</span></li>
+ <li><span style="line-height: 1.5;">Utilisez </span><strong style="line-height: 1.5;">manage.py</strong><span style="line-height: 1.5;"> pour créer une ou plusieurs <em>applications</em> du projet</span><span style="line-height: 1.5;">.</span>
+ <div class="note">
+ <p><strong>Note</strong>: Un site web consiste en une ou plusieurs sections, par exemple un site principal, un blog, un wiki,... La bonne pratique avec Django est de réaliser chacun des composants comme des applications séparées qui pourront éventuellement être réutilisées dans d'autres projets.</p>
+ </div>
+ </li>
+ <li><span style="line-height: 1.5;">Enregistrez la nouvelle application dans le projet. </span></li>
+ <li><span style="line-height: 1.5;">Liez les urls et chemins pour chaque application.</span></li>
+</ol>
+
+<p>Pour <a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">le site web "Bibliothèque locale"</a>, le dossier du site web et celui du projet auront le même nom <em>locallibrary</em>. Une seule application <em>catalog</em> sera utilisée. La hiérachie de dossier du projet à la forme ci-dessous :</p>
+
+<pre class="brush: bash notranslate"><em>locallibrary/ # Website folder</em>
+  <strong>manage.py </strong># Script to run Django tools for this project (created using django-admin)
+  <em>locallibrary/ # Website/project folder </em>(created using django-admin)
+ <em>catalog/ # Application folder </em>(created using manage.py)
+</pre>
+
+<div class="blockIndicator note">
+<p>Afin de respecter la cohérence du code et pouvoir utiliser les développements sur GitHub, les noms du site et des applications, <em>en anglais</em>, n'ont pas été modifiés.</p>
+</div>
+
+<p><span style="line-height: 1.5;">La suite de ce chapitre est consacrée pas à pas aux étapes de création d'un projet et d'une application. La fin du chapitre sera consacré à quelques éléments de configuration du site qui peuvent être réalisés à ce stade.</span></p>
+
+<h2 id="Créer_le_projet_locallibrary">Créer le projet <em>locallibrary</em></h2>
+
+<p>Tout d'abord, il est nécessaire d'ouvrir une fenêtre pour exécuter des commandes en ligne (un terminal sous Linux/MacOS ou une fenêtre command sous Windows). Assurez-vous d'être dans un <a href="/en-US/docs/Learn/Server-side/Django/development_environment#Using_a_virtual_environment">environnement virtuel python</a>, déplacez-vous dans votre arborescence de dossiers pour être dans votre zone de développement des applications Django. Créez-y un sous-dossier pour les projets Django <code>django_projects</code> et déplacez-vous dans ce dernier :</p>
+
+<pre class="brush: bash notranslate">mkdir django_projects
+cd django_projects</pre>
+
+<p>Pour créer un nouveau projet avec le quadriciel Django, il suffit d'utiliser la commande  <code>django-admin startproject</code>. Le résultat de cette commande sera un sous-dossier du nom du projet dans lequel il suffit de s'y déplacer comme indiqué ci-dessous :</p>
+
+<pre class="brush: bash notranslate">django-admin startproject locallibrary
+cd locallibrary</pre>
+
+<p>La commande <code>django-admin</code> crée une arboresence contenant des fichiers et un sous-dossier portant le même nom que le projet :</p>
+
+<pre class="brush: bash notranslate"><em>locallibrary/</em>
+  manage.py
+  <em>locallibrary/</em>
+ __init__.py
+    settings.py
+    urls.py
+    wsgi.py</pre>
+
+<p>Votre répertoire de travail est de la forme :</p>
+
+<pre class="syntaxbox notranslate">../django_projects/locallibrary/</pre>
+
+<p>Le sous-dossier <em>locallibrary</em> permettra de gérer les requêtes web, il contient :</p>
+
+<ul>
+ <li><strong>__init__.py </strong>est un fichier vide qui indique au langage Python de considérer ce dossier comme un module Python.</li>
+ <li><strong>settings.py</strong> contient les paramètrages du site web. C'est ce fichier qui permet de contrôler l'enregistrement des applications créées - qui va être exposé plus bas -, la configuration de la base de données ou des variables globales au site. </li>
+ <li><strong>urls.py</strong> contient les indications de routage des urls du site web. Alors qu'il pourraient contenir toutes les urls, nous verrons plus loin qu'ils est plus pratique de déléguer la gestion des urls à propre à chacune des applications dans le contexte de l'application.<span style="line-height: 1.5;"> </span></li>
+ <li><strong style="line-height: 1.5;">wsgi.py</strong><span style="line-height: 1.5;"> est utilisé pour la gestion de l'interface entre Python et le serveur web. Il est recommandé de ne pas y toucher.</span></li>
+</ul>
+
+<p>Le fichier <strong>manage.py</strong> est utilisé pour créer et gérer les applications au sein du projet. C'est une boîte à outil précieuse qu'il ne faut pas modifier.</p>
+
+<h2 id="Créer_lapplication_catalog">Créer l'application <em>catalog</em></h2>
+
+<p>La commande ci-dessous va créer l'application <em>catalog</em>. Vous devez être dans le dossier de votre projet locallibrary pour exécuter cette commande (dans le même dossier que le fichier <strong>manage.py</strong> de votre projet) :</p>
+
+<pre class="brush: bash notranslate">python3 manage.py startapp catalog</pre>
+
+<div class="note">
+<p><strong>Note</strong>: La commande ci-dessus est exécutée dans un environnement Linux/macOS X. Sous Windows, il se peut que la commande soit : <code>py -3 manage.py startapp catalog</code></p>
+
+<p>Si vous travaillez dans un environnement Windows, l'ensemble de la série didactique est écrite pour un environnement Linux/MacOS. Pensez, alors, à remplacer les commandes <code>python3</code> par <code>py -3</code>.</p>
+
+<p>Si vous utilisez une version postérieure à la version 3.7.0, la commande devrait désormais être  <code>py manage.py startapp catalog</code></p>
+</div>
+
+<p>Cet outil crée un nouveau dossier, du nom de l'application, et le peuple de fichiers essentiels. La plupart des fichiers ont des noms caractéristiques de leur fonction dans le fonctionnement de Django (par exemple, les vues sont traitées dans <strong>views.py</strong>, le modèle de données dans <strong>models.py</strong>, etc.). Ces fichiers contiennent les éléments minimaux nécessaires à leur utilisation dans le projet.</p>
+
+<p>Le dossier projet <em>locallibrary</em> est agrémenté d'un nouveau sous-dossier <em>catalog</em> :</p>
+
+<pre class="brush: bash notranslate"><em>locallibrary/</em>
+  manage.py
+  <em>locallibrary/
+</em><strong>  <em>catalog/</em>
+      admin.py
+      apps.py
+      models.py
+      tests.py
+      views.py
+      __init__.py
+  <em>migrations/</em></strong>
+</pre>
+
+<p>A ceci s'ajoute :</p>
+
+<ul>
+ <li>Un dossier <em>migrations</em>, qui sera utilisé par django pour gérer les migrations et les modifications progressives apportées à la base de données quand des modifications seront faîtes dans les fichiers <em>models.py.</em></li>
+ <li><strong>__init__.py</strong> — est un fichier vide qui indique au langage Python de considérer ce dossier comme un module Python.</li>
+</ul>
+
+<div class="note">
+<p><strong>Note</strong>: Vous pouvez constater que dans le dossier de l'application, il n'y a pas de fichier pour gérer les urls, les gabarits ou les fichiers statiques. Nouys verrons ce point un peu plus loin, ils ne sont pas systématiquement nécessaires.</p>
+</div>
+
+<h2 id="Enregistrer_lapplication_catalog">Enregistrer l'application <em>catalog</em></h2>
+
+<p>Après avoir créé l'application, il est necessaire de l'enregistrée au sein du projet. Ceci permet de prendre en charge l'ensemble des éléments de l'application pour qu'ils soient pris automatiquement en charge par le quadriciel. L'enregistrement se fait dans la section <code>INSTALLED_APPS</code> en ajoutant le nom de l'application à la liste déjà présente.</p>
+
+<p>Éditez le fichier <strong>django_projects/locallibrary/locallibrary/settings.py</strong> et allez jusqu'à la liste <code>INSTALLED_APPS</code>. Ajoutez alors comme indiqué ci-dessous l'application à la liste.</p>
+
+<pre class="brush: bash notranslate">INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+<strong> 'catalog.apps.CatalogConfig', </strong>
+]</pre>
+
+<p>Le nouvel enregistrement défini l'objet pour cette nouvelle application avec le nom (<code>CatalogConfig</code>) qui a été généré dans le fichier <strong>/locallibrary/catalog/apps.py</strong> quand l'application a été créée.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Nous verrons plus loin les autres paramètres de ce fichier(comme <code>MIDDLEWARE</code>). Cela permet la prise en charge par <a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django administration site</a> et donne accès à de nombreuses fonctionnalités (gestion des sessions, de l'authentication, etc).</p>
+</div>
+
+<h2 id="Définir_la_base_de_données">Définir la base de données</h2>
+
+<p>Dès à présent, la base de données doit être décrite. Il est souvent recommandé pour minimiser une transition délicate d'utiliser la même base de données en développement et en production. La documentation concernant les <a href="https://docs.djangoproject.com/fr/2.2/ref/settings/#databases">bases de données</a> prises en charge sont bien décrites sur le site du projet Django.</p>
+
+<p>Le système de gestion de base de données (SGBD) SQLite sera utilisé dans le projet de cette série didactique ; nous n'aurons pas d'accès concurents massifs et ce système ne requiert pas  de paramétrages complémentaires. Ci-dessous la définition dans <strong>settings.py</strong> est nécessaire pour utiliser ce SGBD :</p>
+
+<pre class="brush: python notranslate">DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ }
+}
+</pre>
+
+<h2 id="Dautres_paramètrages_du_projet">D'autres paramètrages du projet</h2>
+
+<p>Le fichier <strong>settings.py</strong> est utilisé pour l'ensemble des paramètres du projet, mais pour le moment nous n'allons nous occuper du fuseau horaire. Le format des fuseaux horaires est le format standard en informatique (<a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Liste des codes - <em>en anglais</em></a>). Changez la variable <code>TIME_ZONE</code> de votre projet avec la chaîne appropriée à votre fuseau, par exemple :</p>
+
+<pre class="brush: python notranslate">TIME_ZONE = 'Europe/Paris'</pre>
+
+<p>Il y a deux paramètres à connaître, même s'il ne seront pas modifiés pour l'instant :</p>
+
+<ul>
+ <li><code>SECRET_KEY</code>. Il s'agit d'une clé utilisée pour la gestion de la sécurité d'un site web par Django. Si vous ne protégez pas cette clé - c'est-à-dire si vous divulguez cette information à des tiers - vous devrez changer cette clé lors de la mise en production. </li>
+ <li><code>DEBUG</code>. Ce paramètre est utilisé pour afficher les journaux de traces en cas d'erreur plutôt qu'une simple erreur HTTP en réponse à une requête. Ce paramètre <strong>doit</strong> être positionné à <code>False</code> lors du passage en production, dans le cas contraire vous divulguerez des informations essentielles à un potentiel attaquant. Pendant la période de développement, il est très utile de la conserver à <code>True</code>.</li>
+</ul>
+
+<h2 id="Configurer_le_routage_des_URLs">Configurer le routage des URLs</h2>
+
+<p>La création du site web s'appuie sur un routage d'URL et une gestion de la cartographie des URLs dans le fichier <strong>urls.py</strong>) présent dans le dossier du projet. Même si vous pouvez directement utiliser ce fichier pour gérer le routage des URLs, il est recommandé d'utiliser un mécanisme de subsidiarité avec une gestion d'URLs par application. En outre cette méthode de délégation permet une meilleure poratbilité de vos développements dans vos différents projets.</p>
+
+<p>A l'ouverture du fichier <strong>locallibrary/locallibrary/urls.py</strong>,  vous pouvez remarquer les premières instructions sur la manière de gérer la cartographie des URLs.</p>
+
+<pre class="brush: python notranslate">"""locallibrary URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.1/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+]
+</pre>
+
+<p>Le routage des URLs est géré à l'aide de la variable <code>urlpatterns</code>. Elle consititue une liste Python de fonctions <code>path()</code>. Chaque instance <code>path()</code> peut associer des motifs d'URL à une vue particulière, qui sera appelée si l'URL appellée correspond au motif décrit, ou vers une autre liste d'URL (dans ce cas, le motif est à considérer comme le motif de base pour le module dans lequel il est décrit). La variable <code>urlpatterns</code> contient au démarrage une seule fonction qui permet de gérer l'URL d'administration du site et utilisant le module par défaut de Django <code>admin.site.urls</code>.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Dans la fonction <code>path()</code>, une route est une chaîne de caractères définissant une URL ou un motif d'URL. Cette chaîne peut inclure des variables nommées (entre &lt; et &gt;, par exemple <code>'catalog/&lt;id&gt;/'</code>). Ce motif correspondra à une URL du type <strong>/catalog/</strong><em>des_caracteres</em><strong>/</strong>. La chaîne <em>des_caracteres</em> sera transmis à la vue comme une chaîne de caractère associée à une variable nommée <code>id</code>. Ce point sera vu en détails plus loin dans la série didactique.</p>
+</div>
+
+<p>Ajoutez les lignes ci-dessous à la fin du fichier de manière à ajouter dans la variable <code>urlpatterns</code> une nouvelle entrée à la liste des routes. Cette nouvelle entrée permet une nouvelle route pour <code>catalog/</code> dont la gestion est déléguée au fichier <strong>urls.py</strong> du module <strong>catalog</strong> (c'est-à-dire le fichier <strong>catalog/urls.py</strong>).</p>
+
+<pre class="brush: python notranslate"># Use include() to add paths from the catalog application
+from django.urls import include
+from django.urls import path
+
+urlpatterns += [
+    path('catalog/', include('catalog.urls')),
+]
+
+</pre>
+
+<p>Il est nécessaire de rediriger la racine du site (concrètement <code>https://127.0.0.1:8000/</code>) vers celui de la seule application <em>catalog</em> qui va être utilisée dans ce projet (concrètemen <code>127.0.0.1:8000/catalog/</code>). Pour cette étape, nous utilisons la fonction particulière (<code>RedirectView</code>) qui prend comme argument le lien relatif (concrètement <code>/catalog/</code>) quand le motif de l'URL correspondra (concrètement la racine du site).</p>
+
+<p>Ajoutez les lignes ci-dessous au bas du fichier <strong>urls.py</strong> :</p>
+
+<pre class="brush: python notranslate">#Add URL maps to redirect the base URL to our application
+from django.views.generic import RedirectView
+urlpatterns += [
+    path('', RedirectView.as_view(url='/catalog/', permanent=True)),
+]
+</pre>
+
+<p>La racine du site ('/') est prise en compte par Django, il est donc inutile d'écrire le chemin avec le caractère '/' en début. Si vous maitenez ce mode d'écriture, vous aurez le message ci-dessous au démarrage du serveur :</p>
+
+<pre class="brush: python notranslate">System check identified some issues:
+
+WARNINGS:
+?: (urls.W002) Your URL pattern '/' has a route beginning with a '/'.
+Remove this slash as it is unnecessary.
+If this pattern is targeted in an include(), ensure the include() pattern has a trailing '/'.
+</pre>
+
+<p>Django ne s'occupe pas nativement de fichiers statiques tels que des fichiers CSS, JavaScript, ou des images, cependant il est très utile pour que le serveur de développement le fasse pendant la création du site. Une dernière étape de configuration du routage générique des urls, consiste donc à gérer la publication des fichiers statiques. </p>
+
+<p>Ajoutez les lignes ci-dessous au bas du fichier <strong>urls.py</strong> :</p>
+
+<pre class="notranslate"><code># Use static() to add url mapping to serve static files during development (only)
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)</code>
+</pre>
+
+<div class="note">
+<p><strong>Note</strong>: Il y a plusieurs manière pour ajouter des routes à la variable <code>urlpatterns</code> (dans les étapes décrites ci-dessus nous avons ajouté petit à patir en utilisant l'opérateur <code>+=</code> pour bien séparer les étapes). Il est en réalité tout à fait possible de toput regrouper dans une seule étape :</p>
+
+<pre class="brush: python notranslate">urlpatterns = [
+  path('admin/', admin.site.urls),
+  path('catalog/', include('catalog.urls')),
+ path('', RedirectView.as_view(url='/catalog/')),
+] + <code>static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)</code>
+</pre>
+
+<p>De même, nous avons ajouté des imports de module à chaque étapes (par exemple, <code>from django.urls import include</code>) ce qui permet de bien voir les différentes étapes. Cependant, l'habitude veut que tous les imports soient traités en début de fichier Python.</p>
+</div>
+
+<p>Dernière étape ! Il faut créer le fichier urls.py dans l'application (ou le module) catalog et de définir la variable <code>urlpatterns</code> vide pour le moment. </p>
+
+<pre class="brush: python notranslate">from django.urls import path
+from . import views
+
+urlpatterns = [
+
+]
+</pre>
+
+<h2 id="Tester_le_site_web">Tester le site web</h2>
+
+<p>A ce niveau, le squelette du site est prêt. Le site ne produit rien de concret mais il peut être démarré pour s'assurer que les modifications apportées ne génèrent pas d'erreur au démarrage du serveur.</p>
+
+<p>Avant de démarer le serveur, et si vous vous souvenez bien, nous devrions faire une mise à niveau de la base de données. Il s'agit de préparer et de faire une migration de la base de données.</p>
+
+<h3 id="Exécuter_la_migartion_de_la_base_de_données">Exécuter la migartion de la base de données</h3>
+
+<p>Django utilise une cartographie d'objet relation ou mapping d'objet relationnel pour simuler une base de données orientée objet avec une base de données relationnelles. Au fur et à mesure des modification qui sont apportées dans la définition du modèle de données, le quadriciel va générer les scripts de migration (ces scripts sont localisés dans <code>locallibrary/catalog/migration</code>) pour modifier les structures de données associées dans la base de données.</p>
+
+<p>Quand le site a été créé (cf. supra), Django a automatiquement ajouté plusieurs modèles de base pour pouvoir administrer le site (point qui sera abordé plus loin). Pour configurer la base de données, avec ces éléments de base, il vous faut exécuter les commandes en ligne ci-dessous dans le répertoire racine du projet (dossier où se trouve<strong> manage.py</strong>):</p>
+
+<pre class="brush: bash notranslate">python3 manage.py makemigrations
+python3 manage.py migrate
+</pre>
+
+<div class="warning">
+<p><strong>Important</strong>: Chaque fois que vous ferez évoluer le modèle de données, vous devrez exécuter le commandes ci-dessus (elles seront traduites en structure dans la base de données que cela conduise à l'ajout ou au retrait d'objets ou d'attributs).</p>
+</div>
+
+<p>L'option <code>makemigrations</code> réalise (sans les appliquer) les migrations nécessaires à toutes les applications du projet. Vous pouvez cependant préciser le nom de l'application pour laquelle vous souhaitez réaliser la migration. Ceci permet de vérifier le code et la cohérence du modèle de donner avant de l'appliquer réellement. Quand vous aurez un niveau expert, vous pourrez choisir de les modifier à la marge.</p>
+
+<p>L'option <code>migrate</code> applique les modifications sur la base de données (Django trace les modifications réalisées dans la base de données).</p>
+
+<div class="note">
+<p><strong>Note</strong>: Vous pouvez consulter la documentation <a href="https://docs.djangoproject.com/fr/2.2/topics/migrations/">Migrations</a> (sur le site Django) pour plus d'informations.</p>
+</div>
+
+<h3 id="Démarrer_le_site_web">Démarrer le site web</h3>
+
+<p>Pendant la phase de développement, vous pouvez tester votre serveur sur un mode local et le consulter avec votre navigateur.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Le serveur local n'est ni robuste ni performant, il n'est donc pas fait pour être utilisé en production, mais il permet d'être autonome pour les travaux de développement. La configuration par défaut de ce serveur est telle que votre site est accessible à l'URL <code>http://127.0.0.1:8000/</code>. Cependant, vous pouvez modifier ces paramètres et pour plus d'information vous pouvez consulter la documentation sur le site Django des commandes <a href="https://docs.djangoproject.com/fr/2.2/ref/django-admin/#runserver">django-admin and manage.py: runserver</a>.</p>
+</div>
+
+<p>Pour démarrer le serveur local, il suffit d'exécuter la commande ci-dessous dans le répertoire du projet (dossier où se trouver <strong>manage.py</strong>) :</p>
+
+<pre class="brush: bash notranslate">python3 manage.py runserver
+
+ Performing system checks...
+
+ System check identified no issues (0 silenced).
+ August 15, 2018 - 16:11:26
+ Django version 2.1, using settings 'locallibrary.settings'
+ Starting development server at http://127.0.0.1:8000/
+ Quit the server with CTRL-BREAK.
+</pre>
+
+<p>Dès que le serveur est actif, vous pouvez utiliser votre navigateur est accéder à l'URL <code>http://127.0.0.1:8000/</code>. Vous devriez accéder à la page d'erreur ci-dessous :</p>
+
+<p><img alt="Django Debug page for Django 2.0" src="https://mdn.mozillademos.org/files/15729/django_404_debug_page.png"></p>
+
+<p>Ne vous inquitez ! Cette erreur était attendue ; elle est due à l'absence de défintion de routes dans le fichier catalog/urls.py ou dans le module <code>catalog.urls</code> module (que nous avons déclaré dans le fichier urls.py du projet). </p>
+
+<div class="note">
+<p><strong>Note</strong>: La page web ci-dessus met en exergue une fonctionnalité utile de Django ; le mode des traces de debogag. Au lieu d'une simple erreur renvoyée par le serveur, celui-ci affiche un écran d'erreur avec des informations utiles pour corriger le développement conduisant à cette erreur d'affichage. Dans le cas présent, l'erreur est due au motif de l'URL qui ne correspond pas à ce qui a été configuré.</p>
+</div>
+
+<p>À ce stade, nous pouvons considérer que le serveur fonctionne !</p>
+
+<div class="note">
+<p><strong>Note</strong>: Chaque fois que vous apportez des modifications significatives, il est important d'exécuter à nouveau un migration et un test du serveur. Cela est assez rapide, pour ne pas s'en priver !</p>
+</div>
+
+<h2 id="Relevez_le_défi...">Relevez le défi...</h2>
+
+<p>Le dossier <strong>catalog/</strong> a été créé automatiquement et contient des fichiers pour les vues, modèles de données, etc. Ouvrez-les pour les consulter. </p>
+
+<p>Comme vous avez pu le constatez plus haut, une route pour l'administration du site (<code>http://127.0.0.1:8000/admin/</code>) existe déjà dans le fichier <strong>urls.py</strong> du projet. Avec votre navigateur web, vous pouvez découvrir ce qui est derrière ce site.</p>
+
+<ul>
+</ul>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Le squelette du site web est entièrement construit à ce stade. Désormais, vous allez pouvoir y ajouter des urls, des vues, des modèles de données, des gabarits et des formulaires.</p>
+
+<p>Maintenant que ceci est fait, <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">le site web Local Library</a> est opérationnel et nous allons passer à la partie codage et développement pour que le site produise ce qu'il est censé faire.</p>
+
+<h2 id="A_voir_aussi...">A voir aussi...</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/intro/tutorial01/">Écriture de votre première application Django, 1ère partie</a>  (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/fr/2.2/ref/applications/#configuring-applications">Applications</a> (Django Docs). Contains information on configuring applications.</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}</p>
+
+<h2 id="Dans_ce_module">Dans ce module</h2>
+
+<ul>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Didactique: Site web "Bibliothèque locale"</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Django didactique Section 2: Créer le squelette du site web</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Models">Django didactique Section 3: Utilisation des modèles de données</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Admin_site">Django didactique Section 4 : Site d'administration de Django</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Home_page">Django didactique Section 5: Créer la page d'accuei</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/fr/learn/server-side/django/testing/index.html b/files/fr/learn/server-side/django/testing/index.html
new file mode 100644
index 0000000000..ab06d584ec
--- /dev/null
+++ b/files/fr/learn/server-side/django/testing/index.html
@@ -0,0 +1,956 @@
+---
+title: 'Django Tutorial Part 10: Testing a Django web application'
+slug: Learn/Server-side/Django/Testing
+tags:
+ - Beginner
+ - CodingScripting
+ - Django Testing
+ - Testing
+ - Tutorial
+ - django
+ - server-side
+ - tests
+ - unit tests
+translation_of: Learn/Server-side/Django/Testing
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Quant un site web grandit, il devient plus difficile à tester manuellement. Non seulement il y a plus de choses à tester, mais encore, comme les interactions entres ses composants deviennent plus complexes, un léger changement dans une partie de l'application peut affecter les autres parties, si bien qu'il va être nécessaire de faire beaucoup de modifications pour s'assurer que tout continue de fonctionner, et qu'aucune erreur ne sera introduite quand il y aura encore plus de modifications. Une façon de limiter ces problèmes est d'écrire des tests automatiques qui puissent être lancés d'une manière simple et fiable à chaque fois que vous faites une modification. Ce tutoriel montre comment automatiser des <em>tests unitaires</em> sur votre site web en utilisant le framework de tests de Django.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis:</th>
+ <td>Avoir terminé tous les tutoriels précédents, y compris <a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objectif:</th>
+ <td>Comprendre comment écrire des tests unitaires pour des sites web basés sur Django.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Vue_densemble">Vue d'ensemble</h2>
+
+<p>Le site <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Local Library</a> a actuellement des pages pour afficher des listes de tous les livres et auteurs, des pages de détail pour les éléments de type <code>Book</code> et <code>Author</code>, une page pour renouveler des <code>BookInstance</code>s, et des pages pour créer, mettre à jour et effacer des éléments de type <code>Author</code> (et également des enregistrements de type <code>Book</code>, si vous avez relevé le <em>défi</em> dans le <a href="/en-US/docs/Learn/Server-side/Django/Forms">tutoriel sur les formulaires</a>). Même avec ce site relativement petit, naviguer manuellement vers chaque page et vérifier <em>superficiellement</em> que tout fonctionne comme prévu peut prendre plusieurs minutes. Quand nous allons faire des modifications et agrandir le site, le temps requis pour vérifier manuellement que tout fonctionne "proprement" va grandir. Si nous continuons comme cela, nous allons sûrement passer beaucoup de temps à tester notre code, et peu à l'améliorer.</p>
+
+<p>Les tests automatiques peuvent vraiment nous aider à régler ce problème. Les avantages évidents sont qu'ils peuvent être lancés bien rapidement que des tests manuels, peuvent réaliser des tests à un niveau bien plus bas de détail, et tester exactement les mêmes fonctionnalités à chaque fois (des testeurs humains sont loin d'être aussi fiables !). Parce qu'ils sont rapides, les tests automatisés peuvent être exécutés plus régulièrement, et si un test échoue, ils pointent exactement vers la partie du code qui n'a pas fonctionné comme prévu.</p>
+
+<p>De plus, les tests automatisés peuvent se comporter comme le premier "utilisateur" de votre code dans le monde réel, vous obligeant à être rigoureux quand vous définissez et documentez la manière dont votre site doit se comporter. Souvent ils constituent une base pour vos exemples et votre documentation. Pour ces raisons, il existe des processus de développement de logiciels qui commencent par la définition et l'implémentation de tests, et ce n'est qu'après que le code est écrit pour atteindre le comportement attendu (par ex. le développement <a href="https://en.wikipedia.org/wiki/Test-driven_development">test-driven</a> et le développement <a href="https://en.wikipedia.org/wiki/Behavior-driven_development">behaviour-driven</a>).</p>
+
+<p>Ce tutoriel montre comment écrire des tests automatisés pour Django, en ajoutant un certain nombre de tests au site web <em>LocalLibrary</em>.</p>
+
+<h3 id="Catégories_de_tests">Catégories de tests</h3>
+
+<p>Il y a beaucoup de genres, de niveaux et de classifications de tests, ainsi que de manières de tester. Les tests automatisés les plus importants sont:</p>
+
+<dl>
+ <dt>Les tests unitaires</dt>
+ <dd>Ils vérifient le comportement fonctionnel de composants individuels, souvent au niveau des classes et des fonctions.</dd>
+ <dt>Les tests de régression</dt>
+ <dd>Ce sont des tests qui reproduisent des bugs historiques. Chaque test a été lancé originellement pour vérifier que le bug a été résolu, et on le relance ensuite pour s'assurer qu'il n'a pas été ré-introduit suite aux changements de code.</dd>
+ <dt>Les test d'intégration</dt>
+ <dd>Ils vérifient comment les groupes de composants fonctionnent quand ils sont utilisés ensemble. Les tests d'intégraion sont attentifs aux interactions souhaitées entre composants, mais pas nécessairement aux opérations internes de chaque composant. Ils peuvent couvrir des groupes simples de composants à travers tout le site.</dd>
+</dl>
+
+<div class="note">
+<p><strong>Note : </strong>Les autres genres habituels de tests comprennent : la boîte noire, la boîte blanche, les tests manuels, automatiques, de canari (canary), de fumée (smoke), de conformité (conformance), d'approbation (acceptance), fonctionnels, système, performance, chargement et stress. Consultez pour plus d'information sur chacun d'eux.</p>
+</div>
+
+<h3 id="Que_fournit_Django_pour_tester">Que fournit Django pour tester ?</h3>
+
+<p>Tester un site web est une tâche complexe, car c'est une opération qui comporte plusieurs couches de logique : depuis la couche HTTP, la gestion des requêtes, les modèles d'interrogation de bases de données, jusqu'à la validation des formulaires, leur traitement et le rendu des templates.</p>
+
+<p>Django fournit un framework de test avec une petite hiérarchie de classes construites sur la librairie standard de Python <code><a href="https://docs.python.org/3/library/unittest.html#module-unittest" title="(in Python v3.5)">unittest</a></code>. Malgré son nom, ce framework de test est utilisable pour les tests unitaires aussi bien que pour les tests d'intégration. Le framework Django ajoute les méthodes et les outils d'une API pour aider à tester un site web et les comportements spécifiques à Django. Ces méthodes vous permettent de simuler des requêtes, d'insérer des données de test et d'inspecter la sortie de votre application. Django fournit aussi une API (<a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>) et des outils pour <a href="https://docs.djangoproject.com/en/2.1/topics/testing/advanced/#other-testing-frameworks">utiliser d'autres frameworks de test</a>. Par exemple vous pouvez intégrer le célèbre framework <a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> pour simuler l'interaction entre un utilisateur et un vrai navigateur.</p>
+
+<p>Pour écrire un test, vous partez de l'une des classes de test de base fournies par Django (ou par <em>unittest</em>) (<a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#simpletestcase">SimpleTestCase</a>, <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#transactiontestcase">TransactionTestCase</a>, <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">TestCase</a>, <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>), et ensuite vous écrivez des méthodes séparées pour vérifier que telle fonctionnalité se comporte comme prévu (les tests utilisent des méthodes "assert" pour vérifier qu'une expression retourne <code>True</code> ou <code>False</code>, ou que deux valeurs sont égales, etc.). Quant vous commencez à lancer un test, le framework exécute les méthodes de test écrites dans vos classes dérivées. Les méthodes de test sont lancées de manière indépendante, avec en commun un réglage initial (<em>setUp</em>) et/ou un comportement de fin (<em>tearDown</em>) définis dans la classe, comme indiqué ci-dessous.</p>
+
+<pre class="brush: python">class YourTestClass(TestCase):
+    def setUp(self):
+  # Setup run before every test method.
+        pass
+
+    def tearDown(self):
+  # Clean up run after every test method.
+        pass
+
+    def test_something_that_will_pass(self):
+        self.assertFalse(False)
+
+    def test_something_that_will_fail(self):
+        self.assertTrue(False)
+</pre>
+
+<p>La meilleure classe de base pour la plupart des tests est <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">django.test.TestCase</a>. Cette classe de test crée une base de données vide avant que ses tests ne soient lancés, et lance toutes les fonctions de test dans sa propre transaction. La classe possède aussi un <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#django.test.Client" title="django.test.Client">Client</a> de test, que vous pouvez utiliser pour simuler l'interaction entre un utilisateur et le code au niveau de la vue. Dans les sections suivantes, nous allons nous concentrer sur les tests unitaires, créés en utilisant la classe de base <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">TestCase</a>.</p>
+
+<div class="note">
+<p><strong>Note :</strong> La classe <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#testcase">django.test.TestCase</a> est très commode, mais peut avoir pour effet de ralentir certains tests plus que nécessaire (tous les tests n'ont pas besoin de créer leur propre base de données ni de simuler une interaction au niveau de la vue). Une fois que vous serez familiarisé avec ce que vous pouvez faire avec cette classe, vous voudrez sans doute remplacer certains de vos tests avec d'autres classes plus simples parmi celles qui sont disponibles.</p>
+</div>
+
+<h3 id="Que_faut-il_tester">Que faut-il tester ?</h3>
+
+<p>Vous pouvez tester tous les aspects de votre code, mais non les librairies ou fonctionnalités faisant partie de Python ou de Django.</p>
+
+<p>Ainsi par exemple, considérez le modèle <code>Author</code> défini ci-dessous. Vous n'avez pas besoin de tester explicitement que <code>first_name</code> et <code>last_name</code> ont été stockés correctement comme <code>CharField</code> dans la base de données, car c'est là quelque chose de défini par Django (cependant, bien sûr, vous allez inévitablement tester cette fonctionnalité pendant le développement). Vous n'avez pas non plus besoin de tester que <code>date_of_birth</code> a été validé comme champ date, car, encore une fois, cela est implémenté par Django.</p>
+
+<p>En revanche, vous pouvez tester que les textes utilisés pour les labels (<em>First name, Last name, Date of birth, Died</em>), ainsi que le respect de la taille allouée au champ texte (100 caractères), car c'est là une partie de votre propre design et quelque chose qui pourrait être cassé/modifié dans le futur.</p>
+
+<pre class="brush: python">class Author(models.Model):
+    first_name = models.CharField(max_length=100)
+    last_name = models.CharField(max_length=100)
+    date_of_birth = models.DateField(null=True, blank=True)
+    date_of_death = models.DateField('Died', null=True, blank=True)
+
+    def get_absolute_url(self):
+        return reverse('author-detail', args=[str(self.id)])
+
+    def __str__(self):
+        return '%s, %s' % (self.last_name, self.first_name)</pre>
+
+<p>De même, vous pouvez tester que les méthodes personnalisées <code style="font-style: normal; font-weight: normal;">get_absolute_url()</code> et <code style="font-style: normal; font-weight: normal;">__str__()</code> se comportent comme prévu, car elles appartiennent à votre logique code/métier. Dans le cas de <code style="font-style: normal; font-weight: normal;">get_absolute_url()</code>, vous pouvez supposer que la méthode Django <code>reverse()</code> a été implémentée correctement, aussi ce que vous allez tester, c'est que la vue associée a été effectivement définie.</p>
+
+<div class="note">
+<p><strong>Note :</strong> Les lecteurs attentifs auront noté que nous pourrions aussi vouloir limiter les dates de naissance et de décès à des valeurs sensibles, et vérifier que le décès intervient après la naissance. En Django, cette contrainte est ajoutée à vos classes de formulaires (bien que vous puissiez définir des validateurs pour les champs du modèle et des validateurs de modèles, ceux-ci ne sont utilisés qu'au niveau du formulaire s'ils sont appelés par la méthode clean() du modèle. Cela requière un ModelForm ou bien la méthode clean() du modèle a besoin d'être appelée explicitement.)</p>
+</div>
+
+<p>Avec cela en tête, commençons à voir comment définir et lancer des tests.</p>
+
+<h2 id="Vue_densemble_de_la_structure_de_test">Vue d'ensemble de la structure de test</h2>
+
+<p>Avant d'entrer dans le détail de "que tester", voyons d'abord brièvement <em>où</em> et <em>comment</em> les tests sont définis.</p>
+
+<p>Django utilise le <a href="https://docs.python.org/3/library/unittest.html#unittest-test-discovery" title="(in Python v3.5)">built-in test discovery</a> du module unittest, qui va chercher des tests, sous le répertoire de travail actuel, dans tous les fichiers dont le nom contient le pattern<strong> test*.py</strong>. Du moment que vous nommez vos fichiers de manière appropriée, vous pouvez utiliser n'importe quelle structure. Nous vous recommandons de créer un module pour coder vos tests, et d'avoir des fichiers distincts pour les modèles, les vues, les formulaires et tout autre type de code que vous avez besoin de tester. Par exemple :</p>
+
+<pre>catalog/
+  /tests/
+  __init__.py
+  test_models.py
+  test_forms.py
+  test_views.py
+</pre>
+
+<p>Créez une structure de fichier comme montré ci-dessus, dans votre projet <em>LocalLibrary</em>. Le ficheir<strong> __init__.py</strong> doit être vide (il dit simplement à Python que ce répertoire est un package). Vous pouvez créer les trois fichiers de test en copiant et renommant le fichier de test du squelette <strong>/catalog/tests.py</strong>.</p>
+
+<div class="note">
+<p><strong>Note :</strong> le fichier de test du squelette <strong>/catalog/tests.py</strong> a été créé automatiquement quand nous avons <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">construit le squelette du site web Django</a>. Il est parfaitement "légal" de mettre tous vos tests dedans, mais si vous testez correctement, vous allez rapidement vous retrouver avec un fichier de test énorme et impossible à gérer.</p>
+
+<p>Supprimez le fichier de squelette, car nous n'en aurons plus besoin.</p>
+</div>
+
+<p>Ouvrez le fichier <strong>/catalog/tests/test_models.py</strong>. Ce fichier doit importer <code>django.test.TestCase</code>, comme indiqué ci-après :</p>
+
+<p>Open <strong>/catalog/tests/test_models.py</strong>. The file should import <code>django.test.TestCase</code>, as shown:</p>
+
+<pre class="brush: python">from django.test import TestCase
+
+# Create your tests here.
+</pre>
+
+<p>Souvent vous voudrez ajouter une classe de test pour chaque modèle/vue/form que vous voulez tester, avec des méthodes individuelles pour tester une fonctionnalité spécifique. Dans d'autres cas vous pourriez souhaiter avoir une class séparée pour tester un cas d'utilisation spécifique, avec des fonctions de test individuelles pour tester les aspects de ce cas d'utilisation (par exemple, une classe pour tester que tel champ d'un modèle est validé correctement, avec des fonctions pour tester chaque possibilité d'échec). Encore une fois, c'est à vous de décider de la structure à adopter, mais elle sera meilleure si vous êtes cohérent.</p>
+
+<p>Ajoutez la classe de test ci-dessous à la fin du fichier. La classe montre comment construire une classe de test de cas dérivant de <code>TestCase</code>.</p>
+
+<pre class="brush: python">class YourTestClass(TestCase):
+    @classmethod
+    def setUpTestData(cls):
+        print("setUpTestData: Run once to set up non-modified data for all class methods.")
+        pass
+
+    def setUp(self):
+        print("setUp: Run once for every test method to setup clean data.")
+        pass
+
+    def test_false_is_false(self):
+        print("Method: test_false_is_false.")
+        self.assertFalse(False)
+
+    def test_false_is_true(self):
+        print("Method: test_false_is_true.")
+        self.assertTrue(False)
+
+    def test_one_plus_one_equals_two(self):
+        print("Method: test_one_plus_one_equals_two.")
+        self.assertEqual(1 + 1, 2)</pre>
+
+<p>La nouvelle classe définit deux méthodes que vous pouvez utiliser pour une configuration pré-test (par exemple, pour créer des modèles ou d'autres objets dont vous aurez besoin pour les tests) :</p>
+
+<ul>
+ <li><code>setUpTestData()</code> est appelée une fois au début du lancement des tests pour un réglage au niveau de la classe. Vous pouvez l'utiliser pour créer des objets qui ne sont pas destinés à être modifiés ou changés dans les méthodes de test.</li>
+ <li><code>setUp()</code> est appelée avant chaque fonction de test pour définir des objets qui peuvent être modifiés par le test (chaque fonction de test obtiendra une version "fraîche" de ces objets).</li>
+</ul>
+
+<div class="note">
+<p>Les classes de test ont aussi une méthode <code>tearDown()</code>, que nous n'avons pas utilisée. Cette méthode n'est pas particulièrement utile pour les tests avec bases de données, dans la mesure où la classe de base <code>TestCase</code> prend soin pour vous de supprimer la base de données utilisées pour les tests.</p>
+</div>
+
+<p>En dessous de ces méthodes, nous avons un certain nombre de méthodes de test, qui utilisent des fonctions <code>Assert</code>, pour tester si certaines conditions sont vraies, fausses ou égales (<code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code>). Si la condition ne renvoie pas le résultat escompté, le test plante et renvoie une erreur à votre console.</p>
+
+<p>Les méthodes <code>AssertTrue</code>, <code>AssertFalse</code> et <code>AssertEqual</code> sont des assertions standard fournies par <strong>unittest</strong>. Il y a d'autres assertions standard dans le framework, et aussi des <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#assertions">assertions spécifiques à Django</a>, pour tester si une vue redirige (<code>assertRedirects</code>), pour tester si tel template a été utilisé (<code>assertTemplateUsed</code>), etc.</p>
+
+<div class="note">
+<p>Normallement vous ne devriez <strong>pas</strong> inclure de fonctions <strong>print()</strong> dans vos tests, comme montré ci-dessus. Nous avons fait cela uniquement pour que vous puissiez voir dans la console (dans la section suivante) l'ordre dans lequel les fonctions de setup sont appelées.</p>
+</div>
+
+<h2 id="Comment_lancer_les_tests">Comment lancer les tests</h2>
+
+<p>La manière la plus facile pour lancer tous les tests est d'utiliser la commande :</p>
+
+<pre class="brush: bash">python3 manage.py test</pre>
+
+<p>Cette commande va lancer la recherche de tous les fichiers ayant la forme <strong>test*.py</strong> sous le répertoire courant, et lancer tous les tests définis, en utilisant les classes de base appropriées (ici nous avons un certain nombre de fichiers de test, mais pour le moment seul <strong>/catalog/tests/test_models.py</strong> contient des tests). Par défaut, chaque test ne fera de rapport qu'en cas d'échec, avec ensuite un résumé du test.</p>
+
+<div class="note">
+<p>Si vous obtenez des erreurs telles que : <code>ValueError: Missing staticfiles manifest entry ...</code>, cela peut être dû au fait que le test ne lance pas <em>collectstatic</em> par défaut, et que votre application utilise une classe de storage qui le requiert (voyez <a href="https://docs.djangoproject.com/en/2.1/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestStaticFilesStorage.manifest_strict">manifest_strict</a> pour plus d'information). Il y a plusieurs façons de remédier à ce problème - la plus facile est de lancer tout simplement <em>collectstatic</em> avant de lancer les tests :</p>
+
+<pre class="brush: bash">python3 manage.py collectstatic
+</pre>
+</div>
+
+<p>Lancez les tests dans le répertoire racine de <em>LocalLibrary</em>. Vous devriez voir une sortie semblable à celle ci-dessous.</p>
+
+<pre class="brush: bash">&gt; python3 manage.py test
+
+Creating test database for alias 'default'...
+<strong>setUpTestData: Run once to set up non-modified data for all class methods.
+setUp: Run once for every test method to setup clean data.
+Method: test_false_is_false.
+setUp: Run once for every test method to setup clean data.
+Method: test_false_is_true.
+setUp: Run once for every test method to setup clean data.
+Method: test_one_plus_one_equals_two.</strong>
+.
+======================================================================
+FAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "D:\Github\django_tmp\library_w_t_2\locallibrary\catalog\tests\tests_models.py", line 22, in test_false_is_true
+    self.assertTrue(False)
+AssertionError: False is not true
+
+----------------------------------------------------------------------
+Ran 3 tests in 0.075s
+
+FAILED (failures=1)
+Destroying test database for alias 'default'...</pre>
+
+<p>Ici nous voyons que nous avons eu un échec pour un test, et nous pouvons voir exactement quelle fonction a planté et pourquoi (cet échec était prévu, car <code>False</code> n'est pas <code>True</code> !).</p>
+
+<div class="note">
+<p><strong>Truc: </strong>La chose la plus importante à apprendre de la sortie de test ci-dessus est qu'il est bien mieux d'utiliser des noms descriptifs/informatifs pour vos objets et méthodes.</p>
+</div>
+
+<p>Le texte en <strong>gras</strong> ci-dessus n'apparaîtra pas normalement dans la sortie de test (elle est générée par les fonctions <code>print()</code> dans nos tests). Cela montre comment la méthode <code>setUpTestData()</code> est appelée une fois pour l'ensemble de classe, tandis que <code>setUp()</code> est appelée avant chaque méthode.</p>
+
+<p>La section suivante mnotre comment vous pouvez lancer des test spécifiques, et comment contrôler la quantité d'information fournie par les tests.</p>
+
+<h3 id="Montrer_plus_dinformations_à_propos_du_test">Montrer plus d'informations à propos du test</h3>
+
+<p>Si vous souhaitez obtenir plus d'informations à propos du test lancé, vous pouvez changer sa <em>verbosité</em>. Par exemple, pour faire la liste de ce qui a fonctionné dans le test, comme de ce qui a échoué (ainsi que tout un tas d'informations sur la manière dont la base de données à été initialisée), vous pouvez mettre la verbosité à "2", comme indiqué ci-dessous :</p>
+
+<pre class="brush: bash">python3 manage.py test --verbosity 2</pre>
+
+<p>Les niveaux de verbosité sont 0, 1, 2 et 3, avec "1" comme valeur par défaut.</p>
+
+<h3 id="Lancer_des_tests_spécifiques">Lancer des tests spécifiques</h3>
+
+<p>Si vous voulez lancer une sous-sélection parmi vos tests, vous pouvez le faire en spécifiant le chemin complet (avec des points) vers le ou les package(s), module, sous-classe de <code>TestCase</code> ou méthode :</p>
+
+<pre class="brush: bash"># Run the specified module
+python3 manage.py test catalog.tests
+
+# Run the specified module
+python3 manage.py test catalog.tests.test_models
+
+# Run the specified class
+python3 manage.py test catalog.tests.test_models.YourTestClass
+
+# Run the specified method
+python3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two
+</pre>
+
+<h2 id="Tests_de_LocalLibrary">Tests de LocalLibrary</h2>
+
+<p>Maintenant que nous savons comment lancer nos tests et quel genre de choses nous avons besoin de tester, regardons quelques exemples pratiques.</p>
+
+<div class="note">
+<p><strong>Note : </strong>Nous n'allons pas écrire tous les tests possibles, mais ce qui suit devrait vous donner une idée sur le fonctionnement des tests, et ce que vous pouvez faire ensuite.</p>
+</div>
+
+<h3 id="Modèles">Modèles</h3>
+
+<p>Comme nous l'avons dit ci-dessus, nous devrions tester tout ce qui relève de notre design, ou tout ce qui est défini par du code que nous avons écrit nous-mêmes, mais pas les bibliothèques ou le code qui est déjà testé par Django ou par l'équipe qui développe Python.</p>
+
+<p>Par exemple, considérez le modèle <code>Author</code> ci-dessous. Ici nous devrions tester les labels de tous les champs, car, bien que nous n'ayons pas explicitement spécifié la plupart d'entre eux, nous avons un design qui dit ce que ces valeurs devraient être. Si nous ne testons pas ces valeurs, nous ne savons pas que les labels des champs ont les valeurs souhaitées. De même, alors que nous sommes tranquilles sur le fait que Django créera un champ de la longueur indiquée, il est intéressant de lancer un test spécifique pour s'assurer qu'il a été implémenté comme prévu.</p>
+
+<pre class="brush: python">class Author(models.Model):
+    first_name = models.CharField(max_length=100)
+    last_name = models.CharField(max_length=100)
+    date_of_birth = models.DateField(null=True, blank=True)
+    date_of_death = models.DateField('Died', null=True, blank=True)
+
+    def get_absolute_url(self):
+        return reverse('author-detail', args=[str(self.id)])
+
+    def __str__(self):
+        return f'{self.last_name}, {self.first_name}'</pre>
+
+<p>Ouvrez notre <strong>/catalog/tests/test_models.py</strong>, et remplacez tout le code qui s'y trouve par le code de test ci-après pour le modèle <code>Author</code>.</p>
+
+<p>Vous voyez que nous importons d'abord <code>TestCase</code> et faisons dériver d'elle notre classe de test (<code>AuthorModelTest</code>) en utilisant un nom descriptif, de façon à pouvoir identifier aisément dans la sortie tout test qui échoue. Nous appelons ensuite <code>setUpTestData()</code> afin de créer un objet <em>author</em>, que nous utiliserons mais que nous ne modifierons jamais dans aucun de nos tests.</p>
+
+<pre class="brush: python">from django.test import TestCase
+
+from catalog.models import Author
+
+class AuthorModelTest(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ # Set up non-modified objects used by all test methods
+ Author.objects.create(first_name='Big', last_name='Bob')
+
+ def test_first_name_label(self):
+ author = Author.objects.get(id=1)
+ field_label = author._meta.get_field('first_name').verbose_name
+ self.assertEquals(field_label, 'first name')
+
+ def test_date_of_death_label(self):
+ author=Author.objects.get(id=1)
+ field_label = author._meta.get_field('date_of_death').verbose_name
+ self.assertEquals(field_label, 'died')
+
+ def test_first_name_max_length(self):
+ author = Author.objects.get(id=1)
+ max_length = author._meta.get_field('first_name').max_length
+ self.assertEquals(max_length, 100)
+
+ def test_object_name_is_last_name_comma_first_name(self):
+ author = Author.objects.get(id=1)
+ expected_object_name = f'{author.last_name}, {author.first_name}'
+ self.assertEquals(expected_object_name, str(author))
+
+ def test_get_absolute_url(self):
+ author = Author.objects.get(id=1)
+ # This will also fail if the urlconf is not defined.
+ self.assertEquals(author.get_absolute_url(), '/catalog/author/1')</pre>
+
+<p>Les tests de champ vérifient que les valeurs des labels de champ (<code>verbose_name</code>) et que la taille des champs de type <em>character</em> sont tels que nous les souhaitons. Ces méthodes ont toutes des noms descriptifs et suivent le même pattern :</p>
+
+<pre class="brush: python"># Get an author object to test
+author = Author.objects.get(id=1)
+
+# Get the metadata for the required field and use it to query the required field data
+field_label = author._meta.get_field('first_name').verbose_name
+
+# Compare the value to the expected result
+self.assertEquals(field_label, 'first name')</pre>
+
+<p>Les choses intéressantes à noter sont :</p>
+
+<ul>
+ <li>Nous ne pouvons obtenir le <code>verbose_name</code> directement en utilisant <code>author.first_name.verbose_name</code>, car <code>author.first_name</code> est une <em>chaîne</em> (non un moyen d'accéder à l'objet <code>first_name</code>, que nous pourrions utiliser pour accéder à ses propriétés). À la place nous devons utiliser l'attribut <code>_meta</code> de author afin d'obtenir une instance de ses champs et utiliser celle-ci pour demander l'information que nous cherchons.</li>
+ <li>Nous avons fait le choix d'utiliser <code>assertEquals(field_label,'first name')</code> plutôt que <code>assertTrue(field_label == 'first name')</code>. La raison en est que, en cas d'échec du test, la sortie vous dira, dans le premier cas, quel est effectivement le label du champ, ce qui rend un peu plus facile le débogage du problème.</li>
+</ul>
+
+<div class="note">
+<p><strong>Note :</strong> Les tests pour les labels de <code>last_name</code> et <code>date_of_birth</code>, ainsi que le test de la longueur du champ <code>last_name</code>, ont été omis. Ajoutez vos propres versions maintenant, en suivant les conventions de nommage et les approches que nous vous avons montrées ci-dessus.</p>
+</div>
+
+<p>Il nous faut également tester nos méthodes personnalisées. Essentiellement, celles-ci vérifient uniquement que le nom de l'objet a été construit comme prévu, en utilisant le format "Last name", "First name", et que l'URL que nous obtenons pour un élément <code>Author</code> est telle que nous l'attendons.</p>
+
+<pre class="brush: python">def test_object_name_is_last_name_comma_first_name(self):
+    author = Author.objects.get(id=1)
+    expected_object_name = f'{author.last_name}, {author.first_name}'
+    self.assertEquals(expected_object_name, str(author))
+
+def test_get_absolute_url(self):
+    author = Author.objects.get(id=1)
+    # This will also fail if the urlconf is not defined.
+    self.assertEquals(author.get_absolute_url(), '/catalog/author/1')</pre>
+
+<p>Maintenant lancez les tests. Si vous avez créé le modèle <code>Author</code> comme décrit dans le tutoriel sur les modèles, il est assez probable que vous allez obtenir une erreur pour le label <code>date_of_death</code>, comme montré ci-dessous. Le test plante parce qu'il a été écrit en s'attendant à ce que la définition du label suive cette convention de Django : ne pas mettre en capitale la première lettre du label (Django le fait pour vous).</p>
+
+<pre class="brush: bash">======================================================================
+FAIL: test_date_of_death_label (catalog.tests.test_models.AuthorModelTest)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+ File "D:\...\locallibrary\catalog\tests\test_models.py", line 32, in test_date_of_death_label
+ self.assertEquals(field_label,'died')
+AssertionError: 'Died' != 'died'
+- Died
+? ^
++ died
+? ^</pre>
+
+<p>C'est vraiment un bug mineur, mais il met en lumière comment écrire des test peut vérifier de plus près les hypothèses que vous pourriez avoir supposées vraies.</p>
+
+<div class="note">
+<p><strong>Note : </strong>Changez en "died" le label pour le champ date_of_death (/catalog/models.py) et relancez les tests.</p>
+</div>
+
+<p>La configuration pour tester les autres modèles est semblable pour tous, aussi nous n'allons pas discuter chacune plus longuement. Sentez-vous libre de créer vos propres tests pour nos autres modèles.</p>
+
+<h3 id="Les_Formulaires">Les Formulaires</h3>
+
+<p>La philosophie pour tester vos formulaires est la même que pour tester vos modèles: vous avez besoin de tester tout ce que vous avez codé ou les spécificités de votre design, mais non le comportement du framework sous-jacent, ni celui des autres bibliothèques tierces.</p>
+
+<p>Généralement, cela signifie que vous devriez tester que les formulaires ont bien les champs que vous voulez, et qu'ils sont rendus avec les bons labels et textes d'aide. Vous n'avez pas besoin de vérifier que Django valide correctement les champs selon leurs types (à moins que vous n'ayez créé vos propres champs personnalisés et leurs validations) ; c'est-à-dire que vous n'avez pas besoin de tester qu'un champ email n'accepte que des emails. Cependant vous pouvez avoir besoin de tester toute validation complémentaire que vous vous attendez à voir réalisée sur les champs, et tout message d'erreur généré par votre code.</p>
+
+<p>Considérez notre formulaire pour renouveler les livres. Il a seulement 1 champ pour la date de renouvellement, qui va avoir un label et un texte d'aide que nous avons besoin de vérifier.</p>
+
+<pre class="brush: python">class RenewBookForm(forms.Form):
+ """Form for a librarian to renew books."""
+ renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+
+ def clean_renewal_date(self):
+ data = self.cleaned_data['renewal_date']
+
+ # Check if a date is not in the past.
+ if data &lt; datetime.date.today():
+ raise ValidationError(_('Invalid date - renewal in past'))
+
+ # Check if date is in the allowed range (+4 weeks from today).
+ if data &gt; 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</pre>
+
+<p>Ouvrez notre fichier <strong>/catalog/tests/test_forms.py</strong>, et remplacez tout le code qui s'y trouve par le code suivant, qui teste le formulaire <code>RenewBookForm</code>. Nous commençons par importer notre formulaire et des bibliothèques Python et Django pour tester les fonctionnalités liées au temps. Ensuite nous déclarons notre classe de test pour formulaire de la même manière que nous l'avons fait pour les modèles, en utilisant un nom descriptif pour notre classe de test dérivée de <code>TestCase</code>.</p>
+
+<pre class="brush: python">import datetime
+
+from django.test import TestCase
+from django.utils import timezone
+
+from catalog.forms import RenewBookForm
+
+class RenewBookFormTest(TestCase):
+    def test_renew_form_date_field_label(self):
+        form = RenewBookForm()
+        self.assertTrue(form.fields['renewal_date'].label == None or form.fields['renewal_date'].label == 'renewal date')
+
+    def test_renew_form_date_field_help_text(self):
+        form = RenewBookForm()
+        self.assertEqual(form.fields['renewal_date'].help_text, 'Enter a date between now and 4 weeks (default 3).')
+
+    def test_renew_form_date_in_past(self):
+        date = datetime.date.today() - datetime.timedelta(days=1)
+        form = RenewBookForm(data={'renewal_date': date})
+        self.assertFalse(form.is_valid())
+
+    def test_renew_form_date_too_far_in_future(self):
+        date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1)
+        form = RenewBookForm(data={'renewal_date': date})
+        self.assertFalse(form.is_valid())
+
+    def test_renew_form_date_today(self):
+        date = datetime.date.today()
+        form = RenewBookForm(data={'renewal_date': date})
+        self.assertTrue(form.is_valid())
+
+    def test_renew_form_date_max(self):
+        date = timezone.localtime() + datetime.timedelta(weeks=4)
+        form = RenewBookForm(data={'renewal_date': date})
+        self.assertTrue(form.is_valid())
+</pre>
+
+<p>Les deux premières fonctions testent que le <code>label</code> et le <code>help_text</code> du champ sont tels qu'on les attend. Nous devons accéder au champ en utilisant le dictionnaire du champ (p. ex. <code>form.fields['renewal_date']</code>). Notez bien ici que nous devons aussi tester si la valeur du label est <code>None</code>, car même si Django rend le label correct, il retournera <code>None</code> si la valeur n'est pas définie <em>explicitement</em>.</p>
+
+<p>Les autres fonctions testent que le formulaire est valide avec des dates de renouvellement situées à l'intérieur des limites acceptables, et invalide avec des valeurs en dehors de ces limites. Notez comment nous construisons des valeurs de dates de test autour de notre date actuelle (<code>datetime.date.today()</code>) en utilisant <code>datetime.timedelta()</code> (dans ce cas en spécifiant un nombre de jours ou de semaines). Ensuite nous créons juste le formulaire en lui passant nos données, et nous testons s'il est valide.</p>
+
+<div class="note">
+<p><strong>Note :</strong> Ici nous n'utilisons pas réellement la base de données ni le client de test. Envisagez de modifier ces tests pour utiliser <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#django.test.SimpleTestCase">SimpleTestCase</a>.</p>
+
+<p>Nous avons aussi besoin de vérifier que les erreurs correctes sont levées si le formulaire est invalide. Cependant cela se fait habituellement dans la partie view, c'est pourquoi nous allons nous y attacher dans la prochaine section.</p>
+</div>
+
+<p>C'est tout pour les formulaires; nous en avons d'autres, mais ils sont automatiquement créés par nos vues génériques pour édition basées sur des classes, et c'est là qu'elles doivent être testées. Lancez les tests et vérifiez que notre code passe toujours !</p>
+
+<h3 id="Vues">Vues</h3>
+
+<p>Pour valider le comportement de notre vue, nous utilisons le <a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/#django.test.Client">client</a> de test de Django. Cette classe se comporte comme un navigateur web fictif que nous pouvons utiliser pour simuler des requêtes <code>GET</code> et <code>POST</code> à une URL donnée, et observer la réponse. Nous pouvons voir à peu près tout au sujet de la réponse, depuis le HTTP bas-niveau (entêtes et codes de statut résultants) jusqu'au template que nous utilisons pour rendre le HTML et aux données de contexte que nous lui passons. Nous pouvons aussi voir la chaîne des redirections (s'il y en a) et vérifier l'URL et le code de statut à chaque étape. Cela nous permet de vérifier que chaque vue se comporte comme prévu.</p>
+
+<p>Commençons avec l'une de nos vues les plus simples, celle qui fournit une liste de tous les auteurs. Elle est affichée à l'URL <strong>/catalog/authors/</strong> (une URL nommée 'authors' dans la configuration des URL).</p>
+
+<pre class="brush: python">class AuthorListView(generic.ListView):
+ model = Author
+ paginate_by = 10
+</pre>
+
+<p>Comme c'est une vue liste générique, presque tout est fait à notre place par Django. Probablement, si vous faites confiance à Django, la seule chose que vous aurez besoin de tester, c'est que la vue est accessible à l'URL correcte et qu'elle peut être atteinte en utilisant son nom. Cependant, si vous utilisez un processus de développement 'test-driven', vous allez commencer par écrire des tests qui confirmeront que la vue affiche bien tous les auteurs, en les paginant par lots de 10.</p>
+
+<p>Ouvrez le fichier <strong>/catalog/tests/test_views.py</strong>, et remplacez tout texte existant par le code de test suivant, pour la vue <code>AuthorListView</code>. Comme auparavant, nous importons notre modèle et quelques classes utiles. Dans la méthode <code>setUpTestData()</code>, nous définissons un certain nombre d'objets <code>Author</code>, de façon à pouvoir tester notre pagination.</p>
+
+<pre class="brush: python">from django.test import TestCase
+from django.urls import reverse
+
+from catalog.models import Author
+
+class AuthorListViewTest(TestCase):
+    @classmethod
+    def setUpTestData(cls):
+        # Create 13 authors for pagination tests
+        number_of_authors = 13
+
+        for author_id in range(number_of_authors):
+            Author.objects.create(
+ first_name=f'Christian {author_id}',
+ last_name=f'Surname {author_id}',
+ )
+
+    def test_view_url_exists_at_desired_location(self):
+        response = self.client.get('/catalog/authors/')
+        self.assertEqual(response.status_code, 200)
+
+    def test_view_url_accessible_by_name(self):
+        response = self.client.get(reverse('authors'))
+        self.assertEqual(response.status_code, 200)
+
+    def test_view_uses_correct_template(self):
+        response = self.client.get(reverse('authors'))
+        self.assertEqual(response.status_code, 200)
+        self.assertTemplateUsed(response, 'catalog/author_list.html')
+
+    def test_pagination_is_ten(self):
+        response = self.client.get(reverse('authors'))
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue('is_paginated' in response.context)
+        self.assertTrue(response.context['is_paginated'] == True)
+        self.assertTrue(len(response.context['author_list']) == 10)
+
+    def test_lists_all_authors(self):
+        # Get second page and confirm it has (exactly) remaining 3 items
+        response = self.client.get(reverse('authors')+'?page=2')
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue('is_paginated' in response.context)
+        self.assertTrue(response.context['is_paginated'] == True)
+        self.assertTrue(len(response.context['author_list']) == 3)</pre>
+
+<p>Tous les tests utilisent le client (qui appartient à notre classe dérivée de <code>TestCase</code>), afin de simuler une requête <code>GET</code> et d'obtenir une réponse. La première version vérifie une URL spécifique (note : seulement le chemin spécifique, sans le domaine), tandis que la seconde génère une URL à partir de son nom tel qu'il se trouve dans la configuration des URL.</p>
+
+<pre class="brush: python">response = self.client.get('/catalog/authors/')
+response = self.client.get(reverse('authors'))
+</pre>
+
+<p>Une fois que nous avons la réponse, nous lui demandons son code de statut, le template utilisé, si la réponse est paginée ou non, le nombre d'éléments retournés et le nombre total d'éléments.</p>
+
+<div class="blockIndicator note">
+<p class="brush: python"><strong>Note :</strong> Si, dans votre fichier <strong>/c</strong><strong>atalog/views.py</strong>, vous mettez la variable <code>paginate_by</code> à un nombre autre que 10, assurez-vous de mettre à jour les lignes qui testent le nombre correct d'éléments affichés dans les templates paginés, ci-dessus et dans les sections qui suivent. Par exemple, si vous mettez à 5 la variable pour la liste des auteurs, changez ainsi la ligne ci-dessus :</p>
+
+<pre class="brush: python">self.assertTrue(len(response.context['author_list']) == 5)
+</pre>
+</div>
+
+<p>La variable la plus intéressante que nous montrons ci-dessus est <code>response.context</code>, qui est la variable de contexte passée au template par la vue. C'est incroyablement utile pour tester, parce qu'elle nous autorise à confirmer que notre template reçoit bien toutes les données dont il a besoin. En d'autres termes, nous pouvons vérifier que nous utilisons le template prévu, et quelles données le template utilise, ce qui permet dans une large mesure de vérifier que tous les problèmes de 'render' sont seulement dus au template.</p>
+
+<h4 id="Vues_limitées_aux_utilisateurs_connectés">Vues limitées aux utilisateurs connectés</h4>
+
+<p>Dans certains cas, vous voudrez tester une vue qui est limitée aux seuls utilisateurs connectés. Par exemple notre vue <code>LoanedBooksByUserListView</code> est très semblable à notre vue précédente, mais n'est accessible qu'aux utilisateurs connectés, et n'affiche que des enregistrements de type <code>BookInstance</code> qui sont empruntés par l'utilisateur courant, ont le statut 'on loan', et sont triés 'le plus ancien en premier'.</p>
+
+<pre class="brush: python">from django.contrib.auth.mixins import LoginRequiredMixin
+
+class LoanedBooksByUserListView(LoginRequiredMixin, generic.ListView):
+ """Generic class-based view listing books on loan to current user."""
+ model = BookInstance
+ template_name ='catalog/bookinstance_list_borrowed_user.html'
+ paginate_by = 10
+
+ def get_queryset(self):
+ return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')</pre>
+
+<p>Ajoutez le code de test suivant à <strong>/catalog/tests/test_views.py</strong>. Ici nous utilisons d'abord la méthode <code>SetUp()</code> pour créer des logins de comptes d'utilisateurs et des objets de type <code>BookInstance</code> (avec leurs livres et autres enregistrements associés), que nous utiliserons plus tard dans les tests. La moitié des livres sont empruntés par chaque utilisateur-test, mais nous avons initialement mis le statut de tous les livres à "maintenance". Nous avons utilisé <code>SetUp()</code> plutôt que <code>setUpTestData()</code>, parce que nous allons modifier plus tard certains de ces objets.</p>
+
+<div class="note">
+<p><strong>Note :</strong> Le code de <code>setUp()</code> ci-dessous crée un livre avec un <code>Language</code> spécifique, mais <em>votre</em> code n'inclut peut-être pas le modèle <code>Language</code>, étant donné que celui-ci a été créé lors d'un <em>défi</em>. Si c'est le cas, commentez simplement les bouts de code qui créent ou importent des objets de type Language. Vous devrez aussi le faire dans la section <code>RenewBookInstancesViewTest</code> qui suit.</p>
+</div>
+
+<pre class="brush: python">import datetime
+
+from django.utils import timezone
+from django.contrib.auth.models import User # Required to assign User as a borrower
+
+from catalog.models import BookInstance, Book, Genre, Language
+
+class LoanedBookInstancesByUserListViewTest(TestCase):
+    def setUp(self):
+        # Create two users
+        test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK')
+        test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+
+ test_user1.save()
+        test_user2.save()
+
+        # Create a book
+        test_author = Author.objects.create(first_name='John', last_name='Smith')
+        test_genre = Genre.objects.create(name='Fantasy')
+        test_language = Language.objects.create(name='English')
+        test_book = Book.objects.create(
+ title='Book Title',
+ summary='My book summary',
+ isbn='ABCDEFG',
+ author=test_author,
+ language=test_language,
+ )
+
+ # Create genre as a post-step
+        genre_objects_for_book = Genre.objects.all()
+  test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.
+        test_book.save()
+
+        # Create 30 BookInstance objects
+        number_of_book_copies = 30
+        for book_copy in range(number_of_book_copies):
+            return_date = timezone.localtime() + datetime.timedelta(days=book_copy%5)
+            the_borrower = test_user1 if book_copy % 2 else test_user2
+            status = 'm'
+            BookInstance.objects.create(
+ book=test_book,
+ imprint='Unlikely Imprint, 2016',
+ due_back=return_date,
+ borrower=the_borrower,
+ status=status,
+ )
+
+    def test_redirect_if_not_logged_in(self):
+        response = self.client.get(reverse('my-borrowed'))
+        self.assertRedirects(response, '/accounts/login/?next=/catalog/mybooks/')
+
+    def test_logged_in_uses_correct_template(self):
+        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
+        response = self.client.get(reverse('my-borrowed'))
+
+        # Check our user is logged in
+        self.assertEqual(str(response.context['user']), 'testuser1')
+        # Check that we got a response "success"
+        self.assertEqual(response.status_code, 200)
+
+        # Check we used correct template
+        self.assertTemplateUsed(response, 'catalog/bookinstance_list_borrowed_user.html')
+</pre>
+
+<p>Pour vérifier que la vue redirige à une page de login si l'utilisateur n'est pas connecté, nous utilisons <code>assertRedirects</code>, comme montré dans <code>test_redirect_if_not_logged_in()</code>. Pour vérifier que la page est affichée pour un utilisateur connecté, nous connectons d'abord notre utilisateur-test, et ensuite nous accédons de nouveau à la page et vérifions que nous obtenons un <code>status_code</code> de 200 (succès).</p>
+
+<p>Le reste des test vérifie que notre vue ne retourne que les livres qui sont prêtés à notre emprunteur courant. Copiez ce code et collez le à la fin de la classe de test ci-dessus.</p>
+
+<pre class="brush: python">    def test_only_borrowed_books_in_list(self):
+        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
+        response = self.client.get(reverse('my-borrowed'))
+
+        # Check our user is logged in
+        self.assertEqual(str(response.context['user']), 'testuser1')
+        # Check that we got a response "success"
+        self.assertEqual(response.status_code, 200)
+
+        # Check that initially we don't have any books in list (none on loan)
+        self.assertTrue('bookinstance_list' in response.context)
+        self.assertEqual(len(response.context['bookinstance_list']), 0)
+
+        # Now change all books to be on loan
+        books = BookInstance.objects.all()[:10]
+
+        for book in books:
+            book.status = 'o'
+            book.save()
+
+        # Check that now we have borrowed books in the list
+        response = self.client.get(reverse('my-borrowed'))
+        # Check our user is logged in
+        self.assertEqual(str(response.context['user']), 'testuser1')
+        # Check that we got a response "success"
+        self.assertEqual(response.status_code, 200)
+
+        self.assertTrue('bookinstance_list' in response.context)
+
+        # Confirm all books belong to testuser1 and are on loan
+        for bookitem in response.context['bookinstance_list']:
+            self.assertEqual(response.context['user'], bookitem.borrower)
+            self.assertEqual('o', bookitem.status)
+
+    def test_pages_ordered_by_due_date(self):
+        # Change all books to be on loan
+        for book in BookInstance.objects.all():
+            book.status='o'
+            book.save()
+
+        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
+        response = self.client.get(reverse('my-borrowed'))
+
+        # Check our user is logged in
+        self.assertEqual(str(response.context['user']), 'testuser1')
+        # Check that we got a response "success"
+        self.assertEqual(response.status_code, 200)
+
+        # Confirm that of the items, only 10 are displayed due to pagination.
+        self.assertEqual(len(response.context['bookinstance_list']), 10)
+
+        last_date = 0
+        for book in response.context['bookinstance_list']:
+            if last_date == 0:
+                last_date = book.due_back
+            else:
+ self.assertTrue(last_date &lt;= book.due_back)
+                last_date = book.due_back</pre>
+
+<p>Vous pourriez aussi ajouter les tests de pagination, si vous voulez !</p>
+
+<h4 id="Tester_des_vues_avec_formulaires">Tester des vues avec formulaires</h4>
+
+<p>Tester des vues avec formulaires est un peu plus compliqué que dans les cas précédents, car vous devez tester un code qui parcourt plus de chemin : l'affichage initial, l'affichage après que la validation des données a échoué, et l'affichage après que la validation a réussi. La bonne nouvelle, c'est que nous utilisons le client de test presque de la même manière que nous l'avons fait pour des vues qui ne font qu'afficher des données.</p>
+
+<p>Pour voir cela, écrivons des tests pour la vue utilisée pour renouveler des livres (<code>renew_book_librarian()</code>) :</p>
+
+<pre class="brush: python">from catalog.forms import RenewBookForm
+
+@permission_required('catalog.can_mark_returned')
+def renew_book_librarian(request, pk):
+    """View function for renewing a specific BookInstance by librarian."""
+    book_instance = 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):
+        book_renewal_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_instance.due_back = form.cleaned_data['renewal_date']
+            book_instance.save()
+
+            # 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)
+        book_renewal_form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})
+
+ context = {
+ 'book_renewal_form': book_renewal_form,
+ 'book_instance': book_instance,
+ }
+
+    return render(request, 'catalog/book_renew_librarian.html', context)</pre>
+
+<p>Nous allons devoir tester que la vue n'est disponible qu'aux utilisateurs ayant la permission <code>can_mark_returned</code>, et que les utilisateurs sont bien redirigés vers une page d'erreur HTTP 404 s'ils essaient de renouveler une <code>BookInstance</code> inexistante. Nous devons vérifier que la valeur initiale du formulaire est remplie avec une date de trois semaines dans le futur, et que si la validation réussit, nous sommes redirigés vers la vue "tous les livres empruntés". Dans le cadre des tests sur l'échec de la validation, nous allons aussi vérifier que notre formulaire envoie les bons messages d'erreur.</p>
+
+<p>Ajoutez la première partie de la classe de test ci-dessous à la fin du fichier <strong>/catalog/tests/test_views.py</strong>. Cela crée deux utilisateurs et deux instances de livres, mais ne donne qu'à un seul utilisateur la permission d'accéder à la vue. Le code pour autoriser les permissions durant les tests est montrée en gras :</p>
+
+<pre class="brush: python">import uuid
+
+from django.contrib.auth.models import Permission # Required to grant the permission needed to set a book as returned.
+
+class RenewBookInstancesViewTest(TestCase):
+ def setUp(self):
+ # Create a user
+ test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK')
+ test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+
+ test_user1.save()
+ test_user2.save()
+
+<strong> permission = Permission.objects.get(name='Set book as returned')
+ test_user2.user_permissions.add(permission)
+ test_user2.save()</strong>
+
+ # Create a book
+ test_author = Author.objects.create(first_name='John', last_name='Smith')
+ test_genre = Genre.objects.create(name='Fantasy')
+ test_language = Language.objects.create(name='English')
+ test_book = Book.objects.create(
+ title='Book Title',
+ summary='My book summary',
+ isbn='ABCDEFG',
+ author=test_author,
+ language=test_language,
+ )
+
+ # Create genre as a post-step
+ genre_objects_for_book = Genre.objects.all()
+ test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.
+ test_book.save()
+
+ # Create a BookInstance object for test_user1
+ return_date = datetime.date.today() + datetime.timedelta(days=5)
+ self.test_bookinstance1 = BookInstance.objects.create(
+ book=test_book,
+ imprint='Unlikely Imprint, 2016',
+ due_back=return_date,
+ borrower=test_user1,
+ status='o',
+ )
+
+ # Create a BookInstance object for test_user2
+ return_date = datetime.date.today() + datetime.timedelta(days=5)
+ self.test_bookinstance2 = BookInstance.objects.create(
+ book=test_book,
+ imprint='Unlikely Imprint, 2016',
+ due_back=return_date,
+ borrower=test_user2,
+ status='o',
+ )</pre>
+
+<p>Ajoutez les tests suivants à la fin de la classe de test. Ils vérifient que seuls les utilisateurs avec les bonnes permissions (<em>testuser2</em>) peuvent accéder à la vue. Nous vérifions tous les cas : quand l'utilisateur n'est pas connecté, quand un utilisateur est connecté mais n'a pas les permissions requises, quand l'utilisateur a les permissions mais n'est pas l'emprunteur (ce test devrait réussir), et ce qui se passe quand ils tentent d'accéder à une <code>BookInstance</code> inexistante. Nous vérifions aussi que le bon template est utilisé.</p>
+
+<pre class="brush: python"> def test_redirect_if_not_logged_in(self):
+        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
+        # Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)
+        self.assertEqual(response.status_code, 302)
+        self.assertTrue(response.url.startswith('/accounts/login/'))
+
+    def test_redirect_if_logged_in_but_not_correct_permission(self):
+        login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')
+        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
+        self.assertEqual(response.status_code, 403)
+
+    def test_logged_in_with_permission_borrowed_book(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance2.pk}))
+
+        # Check that it lets us login - this is our book and we have the right permissions.
+        self.assertEqual(response.status_code, 200)
+
+    def test_logged_in_with_permission_another_users_borrowed_book(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
+
+        # Check that it lets us login. We're a librarian, so we can view any users book
+        self.assertEqual(response.status_code, 200)
+
+    def test_HTTP404_for_invalid_book_if_logged_in(self):
+ # unlikely UID to match our bookinstance!
+        test_uid = uuid.uuid4()
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk':test_uid}))
+        self.assertEqual(response.status_code, 404)
+
+    def test_uses_correct_template(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
+        self.assertEqual(response.status_code, 200)
+
+        # Check we used correct template
+        self.assertTemplateUsed(response, 'catalog/book_renew_librarian.html')
+</pre>
+
+<p>Ajoutez la méthode de test suivante, comme montré ci-dessous. Elle vérifie que la date initiale pour le formulaire est trois semaines dans le futur. Notez comment nous pouvons accéder à la valeur initiale de ce champ de formulaire (en gras).</p>
+
+<pre class="brush: python">    def test_form_renewal_date_initially_has_date_three_weeks_in_future(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))
+        self.assertEqual(response.status_code, 200)
+
+        date_3_weeks_in_future = datetime.date.today() + datetime.timedelta(weeks=3)
+        self.assertEqual(response<strong>.context['form'].initial['renewal_date']</strong>, date_3_weeks_in_future)
+</pre>
+
+<div class="warning">
+<p>Si vous utilisez la class de formulaire <code>RenewBookModelForm(forms.ModelForm)</code> à la place de la classe <code>RenewBookForm(forms.Form)</code>, le nom du champ est <strong>'due_back'</strong> et non <strong>'renewal_date'</strong>.</p>
+</div>
+
+<p>Le test suivant (ajoutez-le à la classe également) vérifie que la vue redirige vers une liste de tous les livres empruntés si le renouvellement réussit. Ce qui diffère ici est que, pour la première fois, nous montrons comment vous pouvez <code>POST</code>er des données en utilisant le client. Les données postées forment le second argument de la fonction post, et elles sont spécifiées comme un dictionnaire de clés/valeurs.</p>
+
+<pre class="brush: python">    def test_redirects_to_all_borrowed_book_list_on_success(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2)
+        <strong>response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future})</strong>
+        self.assertRedirects(response, reverse('all-borrowed'))
+</pre>
+
+<div class="warning">
+<p>La vue <em>all-borrowed</em> a été ajoutée comme <em>défi</em>, et votre code peut, à la place, rediriger vers la page d'accueil '/'. Si c'est le cas, modifiez les deux dernières lignes du code de test pour qu'elles ressemblent au code ci-dessous. L'expression <code>follow=True</code> dans la requête s'assure que la requête retourne l'URL de la destination finale (donc vérifie <code>/catalog/</code> plutôt que <code>/</code>).</p>
+
+<pre class="brush: python"> response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future}, <strong>follow=True</strong> )
+ <strong>self.assertRedirects(</strong><strong>response, '/catalog/')</strong></pre>
+</div>
+
+<p>Copiez les deux dernières fonctions dans la classe, comme indiqué ci-dessous. Elles testent de nouveau des requêtes POST, mais dans ce cas avec des dates de renouvellement invalides. Nous utilisons la méthode assertFormError() pour vérifier que les messages d'erreur sont ceux que nous attendons.</p>
+
+<pre class="brush: python">    def test_form_invalid_renewal_date_past(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        date_in_past = datetime.date.today() - datetime.timedelta(weeks=1)
+        response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': date_in_past})
+        self.assertEqual(response.status_code, 200)
+        <strong>self.assertFormError(</strong><strong>response, 'form', 'renewal_date', 'Invalid date - renewal in past')</strong>
+
+    def test_form_invalid_renewal_date_future(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')
+        invalid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=5)
+        response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': invalid_date_in_future})
+        self.assertEqual(response.status_code, 200)
+        <strong>self.assertFormError(</strong><strong>response, 'form', 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead')</strong>
+</pre>
+
+<p>Le même genre de technique peut être utilisé pour tester les autres vues.</p>
+
+<h3 id="Templates">Templates</h3>
+
+<p>Django fournit des API de test pour vérifier que le bon template sera appelé par vos vues, et pour vous permettre de vérifier que l'information correcte sera envoyée. Il n'y a cependant pas de support d'API spécifique en Django pour tester que votre sortie HTML a le rendu souhaité.</p>
+
+<h2 id="Autres_outils_de_test_recommandés">Autres outils de test recommandés</h2>
+
+<p>Le framework de test de Django peut vous aider à écrire des tests unitaires et d'intégration efficaces - nous n'avons fait que gratter la surface de ce que peut faire unittest,le framework de test sous-jacent, et plus encore les additions de Django (par exemple, regardez comment vous pouvez utiliser unittest.mock pour patcher les bibliothèques tierces afin de tester plus finement votre propre code).</p>
+
+<p>Comme il y a un grand nombre d'autres outils de test à votre disposition, nous ne mentionnerons que les deux suivants :</p>
+
+<ul>
+ <li><a href="http://coverage.readthedocs.io/en/latest/">Coverage</a>: Cet outil Python fait un rapport sur la proportion de votre code réellement exécutée par vos tests. C'est particulièrement intéressant quand vous commencez, et que vous cherchez à vous représenter exactement ce que vous devez tester.</li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> est un framework pour automatiser les tests dans un vrai navigateur. Il vous permet de simuler un utilisateur réel en interaction avec le site, et fournit un excellent framework pour les tests système de votre site (l'étape qui suit les tests d'intégration).</li>
+</ul>
+
+<h2 id="Défi">Défi</h2>
+
+<p>Il y a beaucoup d'autres modèles et vues que nous pouvons tester. Comme exercice simple, essayez de créer un cas de test pour la vue <code>AuthorCreate</code>.</p>
+
+<pre class="brush: python">class AuthorCreate(PermissionRequiredMixin, CreateView):
+ model = Author
+ fields = '__all__'
+ initial = {'date_of_death':'12/10/2016'}
+ permission_required = 'catalog.can_mark_returned'</pre>
+
+<p>Souvenez-vous que vous avez besoin de vérifier tout ce que vous avez spécifié ou ce qui fait partie du design. Cela va inclure qui a accès, la date initiale, le template utilisé, et où la vue redirige en cas de succès.</p>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Écrire un code de test n'est ni très excitant ni très fascinant, et par conséquent ce travail est souvent laissé pour la fin (ou complètement délaissé) par les créateurs de sites web. C'est pourtant un élément essentiel pour vous assurer que, malgré les changements apportés, votre code peut passer à une nouvelle version en toute sécurité, et que sa maintenance est rentable.</p>
+
+<p>Dans ce tutoriel, nous vous avons montré comment écrire et lancer des tests pour vos modèles, formulaires et vues. Plus important, nous avons fourni un bref résumé de ce que vous devez tester, ce qui est souvent la chose la plus difficile à comprendre quand on commence. Il y a beaucoup de choses à savoir, mais avec ce que vous avez déjà appris, vous devriez être capable de créer des tests unitaires efficaces pour vos sites web.</p>
+
+<p>Le prochain (et dernier) tutoriel montre comment vous pouvez déployer votre merveilleux (et entièrement testé !) site web Django.</p>
+
+<h2 id="À_voir_également">À voir également</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.1/topics/testing/overview/">Writing and running tests</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/intro/tutorial05/">Writing your first Django app, part 5 &gt; Introducing automated testing</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/topics/testing/tools/">Testing tools reference</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/topics/testing/advanced/">Advanced testing topics</a> (Django docs)</li>
+ <li><a href="http://toastdriven.com/blog/2011/apr/10/guide-to-testing-in-django/">A Guide to Testing in Django</a> (Toast Driven Blog, 2011)</li>
+ <li><a href="http://test-driven-django-development.readthedocs.io/en/latest/index.html">Workshop: Test-Driven Web Development with Django</a> (San Diego Python, 2014)</li>
+ <li><a href="https://realpython.com/blog/python/testing-in-django-part-1-best-practices-and-examples/">Testing in Django (Part 1) - Best Practices and Examples</a> (RealPython, 2013)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</p>
+
+<h2 id="Dans_ce_module">Dans ce module</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/fr/learn/server-side/django/tutorial_local_library_website/index.html b/files/fr/learn/server-side/django/tutorial_local_library_website/index.html
new file mode 100644
index 0000000000..e7ff3ca5c6
--- /dev/null
+++ b/files/fr/learn/server-side/django/tutorial_local_library_website/index.html
@@ -0,0 +1,101 @@
+---
+title: 'Django Didactique: Site web "Bibliothèque locale"'
+slug: Learn/Server-side/Django/Tutorial_local_library_website
+tags:
+ - Apprentissage
+ - Article
+ - Didacticiel
+ - Débutant
+ - Guide
+ - django
+translation_of: Learn/Server-side/Django/Tutorial_local_library_website
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}</div>
+
+<p>Le premier article de cette série didactique explique ce que vous apprendrez et donne un aperçu du site Web "Bibliothèque locale", un exemple, qui va être utiliser et évoluer dans les articles suivants.</p>
+
+<div></div>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis:</th>
+ <td>La lecture de <a href="/fr/docs/Learn/Server-side/Django/Introduction">l'introduction</a>. Pour les articles suivants avoir mis à jour l'environnement comme décrit précédemment. </td>
+ </tr>
+ <tr>
+ <th scope="row">Objectifs:</th>
+ <td>Présenter l'application à venir et les thèmes qui seront abordés dans cette série didactique.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Vue_d'ensemble">Vue d'ensemble</h2>
+
+<p>La série didactique MDN "Bibliothèque locale" va vous permettre de développer un site web destiné à gérer le catalogue d'une bibliothèque.</p>
+
+<p>Dans les articles qui suivent, vous allez apprendre à :</p>
+
+<ul>
+ <li>Utiliser les outils de Django pour créer le squelette d'un site web et d'applications</li>
+ <li>Démarrer et arrêter le serveur de développement.</li>
+ <li>Créer les modèles de données utilisés par les applications.</li>
+ <li>Utiliser les outils d'administration Django du site web pour y enregsitrer et y peupler les données.</li>
+ <li>Créer des vues pour exploiter en fonction de demandes particulières et restituer à l'aide de modèles les informations dans des documents HTML affichés par votre navigateur.</li>
+ <li>Créer les chemins pour associer des URL avec des vues particulières.</li>
+ <li>Ajouter et gérer les autorisations et le contrôle d'accès au site des utilisateurs.</li>
+ <li>Manipuler les formulaires.</li>
+ <li>Ecrire des jeux de test pour votre application.</li>
+ <li>Utiliser les moyens de sécurité de Django.</li>
+ <li>Déployer en production vote application.</li>
+</ul>
+
+<p>Que vous ayez déjà des connaissance sur le sujet ou que vous ayez aborder succinctement ce quadriciel, à la fin de cette série didactique, vous serez suffisamment autonome pour développer vos propres applications avec Django.</p>
+
+<h2 id="Le_site_web_de_la_Bibliothèque_locale">Le site web de la "Bibliothèque locale"</h2>
+
+<p>La <em>LocalLibrary</em> (Bibliothèque locale) est le nom du site web qui va être créer et qui évoluera tout au long de cette série didatcique. La finalité de ce site web est de diffuser un catalogue des livres en ligne et de permettre aux utilisateurs de le parcourir et de gérer leur propre compte.</p>
+
+<p>Cet exemple a été soigneusement choisi car il permet de progresser en montrant nombre de détails et abordre presque toutes les fonctionnalités de Django. De plus, cet exemple permet d'appréhender progressivement les fonctionnalités les plus importantes du quadriciel :</p>
+
+<ul>
+ <li>Une première étape consistera à définir un catalogue simple qui permet aux utilisateurs de consulter les ouvrages disponibles. Cela combine les schémas classiques et les opérations communes à la plupart de ce type de sites : lire et afficher le contenu d'une base de données...</li>
+ <li>La progression des différents articles permettra d'étudier des fonctions plus avancées du quadriciel. Par exemple, utiliser des formulaires et permettre aux utilisateurs de réserver leurs ouvrages, ceci conduit à mettre en place et utiliser la gestion des utilisateurs et de l'authentification.</li>
+</ul>
+
+<p>Même s'il s'agit d'un sujet extensible, son sujet de <em>Bibliothèque <strong>locale</strong></em> est volontaire. Il s'agit d'aborder rapidement de nombreux sujets de Django en manipulant un minimum d'information. Il s'agit d'enregistrer localement les informations fictives sur les livres, copies, auteurs, etc. Il ne s'agit en aucun cas d'élaborer un produit qui gère, comme pourrait le faire une bibliothèque classique d'autres informations, ni gérer un réseau de bibliothèques comme cela pourrait être le cas avec une <em><strong>grande</strong> biblothèque</em>. </p>
+
+<h2 id="Je_suis_coincé_où_puis-je_trouver_les_sources">Je suis coincé, où puis-je trouver les sources ?</h2>
+
+<p>Au fur et à mesure, les codes et commandes à écrire seront fournis. Ils peuvent être copiés et collés à chaque étapes. Il y aura aussi des codes que vous pourrez compléter avec quelques conseils.</p>
+
+<p>Si vous êtes coincé, vous pourrez trouver une version totalement développée du site sur<a href="https://github.com/mdn/django-locallibrary-tutorial"> Github</a> (<strong>Anglais</strong>).</p>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Vous en savez plus sur le projet <em>LocalLibrary</em> et ce que vous allez progressivement apprendre, il est désormais temps de créer le <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">squellette du projet</a> qui hébergera la bibliothèque.</p>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}</p>
+
+
+
+<h2 id="Dans_ce_module">Dans ce module</h2>
+
+<ul>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Didactique: Site web "Bibliothèque locale"</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/skeleton_website">Django didactique Section 2: Créer le squelette du site web</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Models">Django didactique Section 3: Utilisation des modèles de données</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Admin_site">Django didactique Section 4 : Site d'administration de Django</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Home_page">Django didactique Section 5: Créer la page d'accueil</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/fr/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
diff --git a/files/fr/learn/server-side/django/vues_generiques/index.html b/files/fr/learn/server-side/django/vues_generiques/index.html
new file mode 100644
index 0000000000..f2d48431fc
--- /dev/null
+++ b/files/fr/learn/server-side/django/vues_generiques/index.html
@@ -0,0 +1,634 @@
+---
+title: 'Tutoriel Django - 6e partie : Vues génériques pour les listes et les détails'
+slug: Learn/Server-side/Django/Vues_generiques
+tags:
+ - Beginner
+ - Learn
+ - Tutorial
+ - django
+ - django templates
+ - django views
+translation_of: Learn/Server-side/Django/Generic_views
+---
+<div></div>
+
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</div>
+
+<div>Ce tutoriel améliore notre site web <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a>, en ajoutant des pages de listes et de détails pour les livres et les auteurs. Ici nous allons apprendre les vues génériques basées sur des classes, et montrer comment elles peuvent réduire le volume de code à écrire pour les cas ordinaires. Nous allons aussi entrer plus en détail dans la gestion des URLs, en montrant comment réaliser des recherches de patterns simples.</div>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prérequis:</th>
+ <td>
+ <p>Avoir terminé tous les tutoriels précédents, y compris <a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Objectif:</th>
+ <td>
+ <p>Comprendre où et comment utiliser des vues génériques basées sur classes, et comment extraire des patterns dans des URLs pour transmettre les informations aux vues.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Aperçu">Aperçu</h2>
+
+<p>Dans ce tutoriel, nous allons terminer la première version du site web <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a>, en ajoutant des pages de listes et de détails pour les livres et les auteurs (ou pour être plus précis, nous allons vous montrer comment implémenter les pages concernant les livres, et vous faire créer vous-mêmes les pages concernant les auteurs !).</p>
+
+<p>Le processus est semblable à celui utilisé pour créer la page d'index, processus que nous avons montré dans le tutoriel précédent. Nous allons avoir de nouveau besoin de créer des mappages d'URLs, des vues et des templates. La principale différence est que, pour la page des détails, nous allons avoir le défi supplémentaire d'extraire de l'URL des informations que nous transmettrons à la vue. Pour ces pages, nous allons montrer comment utiliser un type de vue complètement différent : des vues "listes" et "détails" génériques et basées sur des classes. Cela peut réduire significativement la somme de code nécessaire, les rendant ainsi faciles à écrire et à maintenir.</p>
+
+<p>La partie finale de ce tutoriel montrera comment paginer vos données quand vous utilisez des vues "listes" génériques basées sur des classes.</p>
+
+<h2 id="Page_de_liste_de_livres">Page de liste de livres</h2>
+
+<p>La page de liste des livres va afficher une liste de tous les enregistrements de livres disponibles, en utilisant l'URL: <code>catalog/books/</code>. La page va afficher le titre et l'auteur pour chaque enregistrement, et le titre sera un hyperlien vers la page de détails associée. La page aura la même structure et la même zone de navigation que les autres pages du site, et nous pouvons dès lors étendre le template de base (<strong>base_generic.html</strong>) que nous avons créé dans le tutoriel précédent.</p>
+
+<h3 id="Mappage_dURL">Mappage d'URL</h3>
+
+<p>Ouvrez le fichier <strong>/catalog/urls.py</strong>, et copiez-y la ligne en gras ci-dessous. Comme pour la page d'index, cette fonction <code>path()</code> définit un pattern destiné à identifier l'URL (<strong>'books/'</strong>), une fonction vue qui sera appelée si l'URL correspond (<code>views.BookListView.as_view()</code>), et un nom pour ce mappage particulier.</p>
+
+<pre class="brush: python notranslate">urlpatterns = [
+ path('', views.index, name='index'),
+<strong>  </strong>path<strong>('books/', views.BookListView.as_view(), name='books'),</strong>
+]</pre>
+
+<p>Comme discuté dans le tutoriel précédent, l'URL doit auparavant avoir identifié la chaîne <code>/catalog</code>, aussi la vue ne sera réellement appelée que pour l'URL complète: <code>/catalog/books/</code>.</p>
+
+<p>La fonction vue a un format différent de celui que nous avions jusqu'ici : c'est parce que cette vue sera en réalité implémentée sous forme de classe. Nous allons la faire hériter d'une fonction vue générique existante, qui fait la plus grande partie de ce que nous souhaitons réaliser avec cette vue, plutôt que d'écrire notre propre fonction à partir de zéro.</p>
+
+<p>En Django, on accède à la fonction appropriée d'une vue basée sur classe en appelant sa méthode de classe <code>as_view()</code>. Cela a pour effet de créer une instance de la classe, et de s'assurer que les bonnes méthodes seront appelées lors de requêtes HTTP.</p>
+
+<h3 id="Vue_basée_sur_classe">Vue (basée sur classe)</h3>
+
+<p>Nous pourrions assez aisément écrire la vue "liste de livres" comme une fonction ordinaire (comme notre précédente vue "index"), qui interrogerait la base de données pour tous les livres, et qui ensuite appellerait <code>render()</code> pour passer la liste à un template spécifique. À la place, cependant, nous allons utiliser une vue "liste" générique, basée sur une classe (<code>ListView</code>), une classe qui hérite d'une vue existante. Parce que la vue générique implémente déjà la plupart des fonctionnalités dont nous avons besoin et suit les meilleures pratiques Django, nous pourrons créer une vue "liste" plus robuste avec moins de code, moins de répétition, et au final moins de maintenance.</p>
+
+<p>Ouvrez le fichier <strong>catalog/views.py</strong>, et copiez-y le code suivant à la fin :</p>
+
+<pre class="brush: python notranslate">from django.views import generic
+
+class BookListView(generic.ListView):
+ model = Book</pre>
+
+<p>C'est tout ! La vue générique va adresser une requête à la base de données pour obtenir tous les enregistrements du modèle spécifié (<code>Book</code>), et ensuite rendre un template situé à l'adresse <strong>/locallibrary/catalog/templates/catalog/book_list.html</strong> (que nous allons créer ci-dessous). À l'intérieur du template vous pouvez accéder à la liste de livres grâce à la variable de template appelée <code>object_list</code> OU <code>book_list</code> (c'est-à-dire l'appellation générique "<code><em>the_model_name</em>_list</code>").</p>
+
+<div class="note">
+<p><strong>Note </strong>: Ce chemin étrange vers le lieu du template n'est pas une faute de frappe : les vues génériques cherchent les templates dans <code>/<em>application_name</em>/<em>the_model_name</em>_list.html</code> (<code>catalog/book_list.html</code> dans ce cas) à l'intérieur du répertoire <code>/<em>application_name</em>/templates/</code> (<code>/catalog/templates/</code>).</p>
+</div>
+
+<p>Vous pouvez ajouter des attributs pour changer le comportement par défaut utilisé ci-dessus. Par exemple, vous pouvez spécifier un autre fichier de template si vous souhaitez avoir plusieurs vues qui utilisent ce même modèle, ou bien vous pourriez vouloir utiliser un autre nom de variable de template, si book_list n'est pas intuitif par rapport à l'usage que vous faites de vos templates. Probablement, le changement le plus utile est de changer/filtrer le sous-ensemble des résultats retournés : au lieu de lister tous les livres, vous pourriez lister les 5 premiers livres lus par d'autres utilisateurs.</p>
+
+<pre class="brush: python notranslate">class BookListView(generic.ListView):
+ model = Book
+ context_object_name = 'my_book_list' # your own name for the list as a template variable
+ queryset = Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
+ template_name = 'books/my_arbitrary_template_name_list.html' # Specify your own template name/location</pre>
+
+<h4 id="Ré-écrire_des_méthodes_dans_des_vues_basées_sur_classes">Ré-écrire des méthodes dans des vues basées sur classes</h4>
+
+<p>Bien que nous n'ayons pas besoin de le faire ici, sachez qu'il vous est possible de ré-écrire des méthodes de classe.</p>
+
+<p>Par exemple, nous pouvons ré-écrire la méthode <code>get_queryset()</code> pour changer la liste des enregistrements retournés. Cette façon de faire est plus flexible que simplement définir l'attribut <code>queryset</code>, comme nous l'avons fait dans le précédent fragment de code (bien qu'il n'y ait pas vraiment d'intérêt dans ce cas) :</p>
+
+<pre class="brush: python notranslate">class BookListView(generic.ListView):
+ model = Book
+
+    def get_queryset(self):
+        return Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
+</pre>
+
+<p>Nous pourrions aussi réécrire <code>get_context_data()</code>, afin d'envoyer au template des variables de contexte supplémentaires (par défaut c'est la liste de livres qui est envoyée). Le bout de code ci-dessous montre comment ajouter une variable appelée "<code>some_data</code>" au contexte (elle sera alors accessible comme variable de template).</p>
+
+<pre class="brush: python notranslate">class BookListView(generic.ListView):
+ model = Book
+
+    def get_context_data(self, **kwargs):
+        # Call the base implementation first to get the context
+        context = super(BookListView, self).get_context_data(**kwargs)
+        # Create any data and add it to the context
+        context['some_data'] = 'This is just some data'
+        return context</pre>
+
+<p>Quand vous faites cela, il est important de suivre la procédure indiquée ci-dessus :</p>
+
+<ul>
+ <li>D'abord récupérer auprès de la superclasse le contexte existant.</li>
+ <li>Ensuite ajouter la nouvelle information de contexte.</li>
+ <li>Enfin retourner le nouveau contexte (mis à jour).</li>
+</ul>
+
+<div class="note">
+<p><strong>Note </strong>: Voyez dans <a href="https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-display/">Built-in class-based generic views</a> (doc de Django) pour avoir beaucoup plus d'exemples de ce que vous pouvez faire.</p>
+</div>
+
+<h3 id="Créer_le_template_pour_la_Vue_Liste">Créer le template pour la Vue Liste</h3>
+
+<p>Créez le fichier HTML <strong>/locallibrary/catalog/templates/catalog/book_list.html</strong>, et copiez-y le texte ci-dessous. Comme nous l'avons dit ci-dessus, c'est ce fichier que va chercher par défaut la classe générique "liste" basée sur une vue (dans le cas d'un modèle appelé <code>Book</code>, dans une application appelée <code>catalog</code>).</p>
+
+<p>Les templates pour vues génériques sont exactement comme les autres templates (cependant, bien sûr, le contexte et les informations envoyées au templates peuvent être différents). Comme pour notre template <em>index</em>, nous étendons notre template de base à la première ligne, et remplaçons ensuite le bloc appelé <code>content</code>.</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Book List&lt;/h1&gt;
+  <strong>{% if book_list %}</strong>
+  &lt;ul&gt;
+  {% for book in book_list %}
+      &lt;li&gt;
+        &lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+      &lt;/li&gt;
+  {% endfor %}
+  &lt;/ul&gt;
+  <strong>{% else %}</strong>
+  &lt;p&gt;There are no books in the library.&lt;/p&gt;
+  <strong>{% endif %} </strong>
+{% endblock %}</pre>
+
+<p>La vue envoie le contexte (liste de livres), en utilisant par défaut les alias <code>object_list</code> et <code>book_list</code> ; l'un et l'autre fonctionnent.</p>
+
+<h4 id="Exécution_conditionnelle">Exécution conditionnelle</h4>
+
+<p>Nous utilisons les balises de templates <code><a href="https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#if">if</a></code>, <code>else</code>, et <code>endif</code> pour vérifier que la <code>book_list</code> a été définie et n'est pas vide. Si <code>book_list</code> est vide, alors la condition <code>else</code> affiche un texte expliquant qu'il n'y a pas de livres à lister. Si <code>book_list</code> n'est pas vide, nous parcourons la liste de livres.</p>
+
+<pre class="brush: html notranslate"><strong>{% if book_list %}</strong>
+ &lt;!-- code here to list the books --&gt;
+<strong>{% else %}</strong>
+ &lt;p&gt;There are no books in the library.&lt;/p&gt;
+<strong>{% endif %}</strong>
+</pre>
+
+<p>La condition ci-dessus ne vérifie qu'un seul cas, mais vous pouvez ajouter d'autres tests grâce à la balise de template <code>elif</code> (par exemple <code>{% elif var2 %}</code>). Pour plus d'information sur les opérateurs conditionnels, voyez ici :  <a href="https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#if">if</a>, <a href="https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#ifequal-and-ifnotequal">ifequal/ifnotequal</a>, et <a href="https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#ifchanged">ifchanged</a> dans <a href="https://docs.djangoproject.com/en/2.1/ref/templates/builtins">Built-in template tags and filters</a> (Django Docs).</p>
+
+<h4 id="Boucles_for">Boucles for</h4>
+
+<p>Le template utilise les balises de template <a href="https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#for">for</a> et <code>endfor</code> pour boucler à travers la liste de livres, comme montré ci-dessous. Chaque itération peuple la variable de template <code>book</code> avec l'information concernant l'élément courant de la liste.</p>
+
+<pre class="brush: html notranslate">{% for <strong>book</strong> in book_list %}
+ &lt;li&gt; &lt;!-- code here get information from each <strong>book</strong> item --&gt; &lt;/li&gt;
+{% endfor %}
+</pre>
+
+<p>Bien que nous ne l'utilisions pas ici, Django, à l'intérieur de la boucle, va aussi créer d'autres variables que vous pouvez utiliser pour suivre l'itération. Par exemple, vous pouvez tester la variable <code>forloop.last</code> pour réaliser une action conditionnelle au dernier passage de la boucle.</p>
+
+<h4 id="Accéder_aux_variables">Accéder aux variables</h4>
+
+<p>Le code à l'intérieur de la boucle crée un élément de liste pour chaque livre, élément qui montre à la fois le titre (comme lien vers la vue détail, encore à créer), et l'auteur.</p>
+
+<pre class="brush: html notranslate">&lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+</pre>
+
+<p>Nous accédont aux <em>champs</em> de l'enregistrement "livre" associé, en utilisant la notation "à points" (par exemple <code>book.title</code> et <code>book.author</code>), où le texte suivant l'item <code>book</code> est le nom du champ (comme défini dans le modèle).</p>
+
+<p>Nous pouvons aussi appeler des <em>fonctions</em> contenues dans le modèle depuis l'intérieur de notre template — dans ce cas nous appelons <code>Book.get_absolute_url()</code> pour obtenir une URL que vous pouvez utiliser pour afficher dans la vue détail l'enregistrement associé. Cela fonctionne, pourvu que la fonction ne comporte pas d'arguments (il n'y a aucun moyen de passer des arguments !).</p>
+
+<div class="note">
+<p><strong>Note</strong> : Il nous faut être quelque peu attentif aux "effets de bord" quand nous appelons des fonctions dans nos templates. Ici nous récupérons simplement une URL à afficher, mais une fonction peut faire à peu près n'importe quoi — nous ne voudrions pas effacer notre base de données (par exemple) juste parce que nous avons affiché notre template !</p>
+</div>
+
+<h4 id="Mettre_à_jour_le_template_de_base">Mettre à jour le template de base</h4>
+
+<p>Ouvrez le template de base (<strong>/locallibrary/catalog/templates/<em>base_generic.html</em></strong>) et insérez <strong>{% url 'books' %}</strong> dans le lien URL pour <strong>All books</strong>, comme indiqué ci-dessous. Cela va afficher le lien dans toutes les pages (nous pouvons mettre en place ce lien avec succès, maintenant que nous avons créé le mappage d'URL "books").</p>
+
+<pre class="brush: python notranslate">&lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+<strong>&lt;li&gt;&lt;a href="{% url 'books' %}"&gt;All books&lt;/a&gt;&lt;/li&gt;</strong>
+&lt;li&gt;&lt;a href=""&gt;All authors&lt;/a&gt;&lt;/li&gt;</pre>
+
+<h3 id="À_quoi_cela_ressemble-t-il">À quoi cela ressemble-t-il ?</h3>
+
+<p>Vous ne pouvez pas encore construire la liste des livres, car il nous manque toujours une dépendance, à savoir le mappage d'URL pour la page de détail de chaque livre, qui est requise pour créer des hyperliens vers chaque livre. Nous allons montrer les vues liste et détail après la prochaine section.</p>
+
+<h2 id="Page_de_détail_dun_livre">Page de détail d'un livre</h2>
+
+<p>La page de détail d'un livre va afficher les informations sur un livre précis, auquel on accède en utilisant l'URL <code>catalog/book/<em>&lt;id&gt;</em></code> (où <code><em>&lt;id&gt;</em></code> est la clé primaire pour le livre). En plus des champs définis dans le modèle <code>Book</code> (auteur, résumé, ISBN, langue et genre), nous allons aussi lister les détails des copies disponibles (<code>BookInstances</code>), incluant le statut, la date de retour prévue, la marque d'éditeur et l'id. Cela permettra à nos lecteurs, non seulement de s'informer sur le livre, mais aussi de confirmer si et quand il sera disponible.</p>
+
+<h3 id="Mappage_dURL_2">Mappage d'URL</h3>
+
+<p>Ouvrez <strong>/catalog/urls.py</strong> et ajoutez-y le mappeur d'URL '<strong>book-detail</strong>' indiqué en gras ci-dessous. Cette fonction <code>path()</code> définit un pattern, la vue générique basée sur classe qui lui est associée, ainsi qu'un nom.</p>
+
+<pre class="brush: python notranslate">urlpatterns = [
+ path('', views.index, name='index'),
+    path('books/', views.BookListView.as_view(), name='books'),
+<strong>  path('book/&lt;int:pk&gt;', views.BookDetailView.as_view(), name='book-detail'),</strong>
+]</pre>
+
+<p>Pour le chemin <em>book-detail</em>, le pattern d'URL utilise une syntaxe spéciale pour capturer l'id exact du livre que nous voulons voir. La syntaxe est très simple : les chevrons ('&lt;' et '&gt;') définissent la partie de l'URL qui doit être capturée et encadrent le nom de la variable que la vue pourra utiliser pour accéder aux données capturées. Par exemple, <strong>&lt;something&gt;</strong>  va capturer le pattern marqué et passer la valeur à la vue en tant que variable "something". De manière optionnelle, vous pouvez faire précéder le nom de variable d'une <a href="https://docs.djangoproject.com/en/2.1/topics/http/urls/#path-converters">spécification de convertisseur</a>, qui définit le type de la donnée (int, str, slug, uuid, path).</p>
+
+<p>Dans ce cas, nous utilisons <code>'&lt;int:pk&gt;'</code> pour capturer l'id du livre, qui doit être une chaîne formatée d'une certaine manière, et passer cet id à la vue en tant que paramètre nommé <code>pk</code> (abbréviation pour primary key - clé primaire). C'est l'id qui doit être utilisé pour stocker le livre de manière unique dans la base de données, comme défini dans le modèle Book.</p>
+
+<div class="note">
+<p><strong>Note </strong>: Comme nous l'avons dit précédemment, notre URL correcte est en réalité <code>catalog/book/&lt;digits&gt;</code> (comme nous sommes dans l'application <strong>catalog</strong>, <code>/catalog/</code> est supposé).</p>
+</div>
+
+<div class="warning">
+<p><strong>Important</strong> : La vue générique basée sur classe "détail" <em>s'attend</em> à recevoir un paramètre appelé <strong>pk</strong>. Si vous écrivez votre propre fonction, vous pouvez utiliser le nom que vous voulez pour votre paramètre, ou même passer l'information avec un argument non nommé.</p>
+</div>
+
+<h4 id="Introduction_aux_chemins_et_expressions_régulières_avancés">Introduction aux chemins et expressions régulières avancés</h4>
+
+<div class="note">
+<p><strong>Note </strong>: Vous n'aurez pas besoin de cette section pour achever le tutoriel ! Nous en parlons parce que nous savons que cette option vous sera probablement utile dans votre avenir centré sur Django.</p>
+</div>
+
+<p>La recherche de pattern fournie par <code>path()</code> est simple et utile pour les cas (très communs) où vous voulez seulement capturer <em>n'importe quelle</em> chaîne ou entier. Si vous avez besoin d'un filtre plus affiné (par exemple pour filtrer seulement les chaînes qui ont un certain nombre de caractères), alors vous pouvez utiliser la méthode <a href="https://docs.djangoproject.com/en/2.1/ref/urls/#django.urls.re_path">re_path()</a>.</p>
+
+<p>Cette méthode est utilisée exactement comme <code>path()</code>, sauf qu'elle vous permet de spécifier un pattern utilisant une <a href="https://docs.python.org/3/library/re.html">Expression régulière</a>. Par exemple, le chemin précédent pourrait avoir été écrit ainsi :</p>
+
+<pre class="brush: python notranslate"><strong>re_path(r'^book/(?P&lt;pk&gt;\d+)$', views.BookDetailView.as_view(), name='book-detail'),</strong>
+</pre>
+
+<p>Les <em>expressions régulières</em> sont un outil de recherche de pattern extrêmement puissant. Ils sont, il est vrai, assez peu intuitifs et peuvent se révéler intimidants pour les débutants. Ci-dessous vous trouverez une introduction très courte !</p>
+
+<p>La première chose à savoir est que les expressions régulières devraient ordinairement être déclarées en utilisant la syntaxe "chaîne littérale brute" (c'est-à-dire encadrées ainsi : <strong>r'&lt;votre texte d'expression régulière va ici&gt;'</strong>).</p>
+
+<p>L'essentiel de ce que vous aurez besoin de savoir pour déclarer une recherche de pattern est contenu dans le tableau qui suit :</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">Symbol</th>
+ <th scope="col">Meaning</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>^</td>
+ <td>Recherche le début du texte.</td>
+ </tr>
+ <tr>
+ <td>$</td>
+ <td>Recherche la fin du texte.</td>
+ </tr>
+ <tr>
+ <td>\d</td>
+ <td>Recherche un digit (0, 1, 2, ... 9).</td>
+ </tr>
+ <tr>
+ <td>\w</td>
+ <td>Recherche un caractère de mot, c'est-à-dire tout caractère dans l'alphabet (majuscule ou minuscule), un digit ou un underscore (_).</td>
+ </tr>
+ <tr>
+ <td>+</td>
+ <td>Recherche au moins une occurence du caractère précédent. Par exemple, pour rechercher au moins 1 digit, vous utiliseriez <code>\d+</code>. Pour rechercher au moins 1 caractère "a", vous utiliseriez <code>a+</code>.</td>
+ </tr>
+ <tr>
+ <td>*</td>
+ <td>Recherche zéro ou plus occurrence(s) du caractère précédent. Par exemple, pour rechercher "rien ou un mot", vous pourriez utiliser <code>\w*</code>.</td>
+ </tr>
+ <tr>
+ <td>( )</td>
+ <td>Capture la partie du pattern contenue dans les parenthèses. Toutes les valeurs capturées seront passées à la vue en tant que paramètres non nommés (si plusieurs patterns sont capturés, les paramètres associés seront fournis dans l'ordre de déclaration des captures).</td>
+ </tr>
+ <tr>
+ <td>(?P&lt;<em>name</em>&gt;...)</td>
+ <td>Capture le pattern (indiqué par …) en tant que variable nommée (dans ce cas "name"). Les valeurs capturées sont passées à la vue avec le nom spécifié. Votre vue doit par conséquent déclarer un argument avec le même nom !</td>
+ </tr>
+ <tr>
+ <td>[  ]</td>
+ <td>Recherche l'un des caractères contenus dans cet ensemble. Par exemple, [abc] va rechercher "a" ou "b" ou "c". [-\w] va rechercher le caractère "-" ou tout caractère de mot.</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>La plupart des autres caractères peuvent être pris littéralement.</p>
+
+<p>Considérons quelques exemples réels de patterns :</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">Pattern</th>
+ <th scope="col">Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><strong>r'^book/(?P&lt;pk&gt;\d+)$'</strong></td>
+ <td>
+ <p>C'est là l'expression régulière utilisée dans notre mappeur d'URL. Elle recherche une chaîne qui a <code>book/</code> au commencement de la ligne (<strong>^book/</strong>), ensuite a au moins 1 digit (<code>\d+</code>), et enfin se termine (avec aucun caractère non-digit avant la fin du marqueur de ligne).</p>
+
+ <p>Elle capture aussi tous les digits <strong>(?P&lt;pk&gt;\d+)</strong> et les passe à la vue dans un paramètre appelé 'pk'. <strong>Les valeurs capturées sont toujours passées comme des chaînes !</strong></p>
+
+ <p>Par exemple, cette expression régulière trouverait une correspondance dans l'URL <code>book/1234</code>, et enverrait alors une variable <code>pk='1234'</code> à la vue.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(\d+)$'</strong></td>
+ <td>
+ <p>Ceci rechercher la même URL que dans le cas précédent. L'information capturée serait envoyée à la vue en tant qu'argument non nommé.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(?P&lt;stub&gt;[-\w]+)$'</strong></td>
+ <td>
+ <p>Ceci recherche une chaîne qui a <code>book/</code> au commencement de la ligne (<strong>^book/</strong>), ensuite a au moins 1 caractère étant <em>soit</em> un '-', <em>soit</em> un caractère de mot (<strong>[-\w]+</strong>), puis la fin. Ce pattern capture aussi cet ensemble de caractères et le passe à la vue en tant que paramètre nommé 'stub'.</p>
+
+ <p>Ceci est un pattern relativement typique pour un "stub". Les stubs sont des clés primaires basées sur des mots (plus agréables que des IDs) pour retrouver des données. Vous pouvez utiliser un stub si vous voulez que votre URL de livre contienne plus d'informations. Par exemple <code>/catalog/book/the-secret-garden</code>, plutôt que <code>/catalog/book/33</code>.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Vous pouvez capturer plusieurs patterns en une seule fois, et donc encoder beaucoup d'informations différentes dans l'URL.</p>
+
+<div class="note">
+<p><strong>Note </strong>: Comme défi, essayez d'envisager comment vous devriez encoder une URL pour lister tous les livres sortis en telle année, à tel mois et à tel jour, et l'expression régulière qu'il faudrait utiliser pour la rechercher.</p>
+</div>
+
+<h4 id="Passer_des_options_supplémentaires_dans_vos_mappages_dURL">Passer des options supplémentaires dans vos mappages d'URL</h4>
+
+<p>Une fonctionnalité que nous n'avons pas utilisée ici, mais que vous pourriez trouver valable, c'est que vous pouvez passer à la vue des <a href="https://docs.djangoproject.com/en/2.1/topics/http/urls/#views-extra-options">options supplémentaires</a>. Les options sont déclarées comme un dictionnaire que vous passez comme troisième argument (non nommé) à la fonction <code>path()</code>. Cette approche peut être utile si vous voulez utiliser la même vue pour des ressources multiples, et passer des données pour configurer son comportement dans chaque cas (ci-dessous nous fournissons un template différent dans chaque cas).</p>
+
+<pre class="brush: python notranslate">path('url/', views.my_reused_view, <strong>{'my_template_name': 'some_path'}</strong>, name='aurl'),
+path('anotherurl/', views.my_reused_view, <strong>{'my_template_name': 'another_path'}</strong>, name='anotherurl'),
+</pre>
+
+<div class="note">
+<p><strong>Note :</strong> Les options supplémentaires aussi bien que les patterns capturés sont passés à la vue comme arguments <em>nommés</em>. Si vous utilisez le<strong> </strong><strong>même nom</strong> pour un pattern capturé et une option supplémentaire, alors seul la value du pattern capturé sera envoyé à la vue (la valeur spécifiée dans l'option supplémentaire sera abandonnée).</p>
+</div>
+
+<h3 id="Vue_basée_sur_classe_2">Vue (basée sur classe)</h3>
+
+<p>Ouvrez <strong>catalog/views.py</strong>, et copiez-y le code suivant à la fin du fichier :</p>
+
+<pre class="brush: python notranslate">class BookDetailView(generic.DetailView):
+    model = Book</pre>
+
+<p>C'est tout ! La seule chose que vous avez à faire maintenant, c'est créer un template appelé <strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>, et la vue va lui passer les informations de la base de donnée concernant l'enregistrement <code>Book</code> spécifique, extrait par le mapper d'URL. À l'intérieur du template, vous pouvez accéder à la liste de livres via la variable de template appelée <code>object</code> OU <code>book</code> (c'est-à-dire, de manière générique, "le_nom_du_modèle").</p>
+
+<p>Si vous en avez besoin, vous pouvez changer le template utilisé et le nom de l'objet-contexte utilisé pour désigner le livre dans le template. Vous pouvez aussi renommer les méthodes pour, par exemple, ajouter des informations supplémentaires au contexte.</p>
+
+<h4 id="Que_se_passe-t-il_si_lenregistrement_nexiste_pas">Que se passe-t-il si l'enregistrement n'existe pas ?</h4>
+
+<p>Si l'enregistrement demandé n'existe pas, alors la vue générique basée sur classe "détail" va lever automatiquement pour vous une exception <code>Http404</code> — en production, cela va automatiquement afficher une page appropriée "ressource non trouvée", que vous pouvez personnaliser si besoin.</p>
+
+<p>Juste pour vous donner une idée de la manière dont tout cela fonctionne, le morceau de code ci-dessous montre comment vous implémenteriez cette vue comme une fonction si vous n'utilisiez <strong>pas</strong> la vue générique basée sur classe "détail".</p>
+
+<pre class="brush: python notranslate">def book_detail_view(request, primary_key):
+ try:
+ book = Book.objects.get(pk=primary_key)
+ except Book.DoesNotExist:
+ raise Http404('Book does not exist')
+
+ return render(request, 'catalog/book_detail.html', context={'book': book})
+</pre>
+
+<p>La vue essaie d'abord d'obtenir du modèle l'enregistrement correspondant au livre spécifié. Si cela échoue, la vue devrait lever une exception <code>Http404</code> pour indiquer que le livre est "non trouvé". L'étape finale est ensuite, comme d'habitude, d'appeler <code>render()</code> avec le nom du template et les données concernant le livre dans le paramètre <code>context</code> (comme un dictionnaire).</p>
+
+<p>Une alternative consiste à utiliser la fonction <code>get_object_or_404()</code> comme un raccourci pour lever une exception <code>Http404</code> si l'enregistrement n'existe pas.</p>
+
+<pre class="brush: python notranslate">from django.shortcuts import get_object_or_404
+
+def book_detail_view(request, primary_key):
+  book = get_object_or_404(Book, pk=primary_key)
+ return render(request, 'catalog/book_detail.html', context={'book': book})</pre>
+
+<h3 id="Créerle_template_de_la_Vue_Détail">Créerle template de la Vue Détail</h3>
+
+<p>Créez le fichier HTML <strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>, et copiez-y le code ci-dessous. Comme on l'a dit plus haut, c'est là le nom de template attendu par défaut par la vue générique basée sur classe <em>detail</em> (pour un modèle appelé <code>Book</code> dans une application appelée <code>catalog</code>).</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Title: \{{ book.title }}&lt;/h1&gt;
+
+ &lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href=""&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt; &lt;!-- author detail link not yet defined --&gt;
+ &lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; \{{ book.summary }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;ISBN:&lt;/strong&gt; \{{ book.isbn }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Language:&lt;/strong&gt; \{{ book.language }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Genre:&lt;/strong&gt; \{{ book.genre.all|join:", " }}&lt;/p&gt;
+
+ &lt;div style="margin-left:20px;margin-top:20px"&gt;
+ &lt;h4&gt;Copies&lt;/h4&gt;
+
+ {% for copy in book.bookinstance_set.all %}
+ &lt;hr&gt;
+ &lt;p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}"&gt;
+ \{{ copy.get_status_display }}
+ &lt;/p&gt;
+ {% if copy.status != 'a' %}
+ &lt;p&gt;&lt;strong&gt;Due to be returned:&lt;/strong&gt; \{{ copy.due_back }}&lt;/p&gt;
+ {% endif %}
+ &lt;p&gt;&lt;strong&gt;Imprint:&lt;/strong&gt; \{{ copy.imprint }}&lt;/p&gt;
+ &lt;p class="text-muted"&gt;&lt;strong&gt;Id:&lt;/strong&gt; \{{ copy.id }}&lt;/p&gt;
+ {% endfor %}
+ &lt;/div&gt;
+{% endblock %}</pre>
+
+<ul>
+</ul>
+
+<div class="note">
+<p>Le lien vers l'auteur dans le template ci-dessus est vide, parce que nous n'avons pas encore crée de page détail pour un auteur. Une fois que cette page sera créée, vous pourrez remplacer l'URL par ceci :</p>
+
+<pre class="notranslate">&lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;
+</pre>
+</div>
+
+<p>Bien qu'en un peu plus grand, presque tout ce qu'il y a dans ce template a été décrit précédemment :</p>
+
+<ul>
+ <li>Nous étendons notre template de base et récrivons le block "content".</li>
+ <li>Nous utilisons une procédure conditionnelle pour déterminer s'il faut ou non afficher tel contenu spécifique.</li>
+ <li>Nous utilisons une boucle <code>for</code> pour boucler sur des listes d'objets.</li>
+ <li>Nous accédons aux champs du contexte en utilisant la notation à point (parce que nous avons utilisé la vue générique <em>detail</em>, le contexte est nommé <code>book</code> ; nous pourrions aussi utiliser "<code>object</code>").</li>
+</ul>
+
+<p>Une chose intéressante que nous n'avons pas encore vue, c'est la fonction <code>book.bookinstance_set.all()</code>. Cette méthode est "automagiquement" construite par Django pour retourner l'ensemble des enregistrements <code>BookInstance</code> associés à un <code>Book</code> particulier.</p>
+
+<pre class="brush: python notranslate">{% for copy in book.bookinstance_set.all %}
+ &lt;!-- code to iterate across each copy/instance of a book --&gt;
+{% endfor %}</pre>
+
+<p>Cette méthode est requise parce que vous déclarez un champ <code>ForeignKey</code> (one-to-many) seulement du côté "one" de la relation. Comme vous ne faites rien pour déclarer la relation dans les modèles opposés ("many"), Django n'a pas de champ pour récupérer l'ensemble des enregistrements associés. Pour remédier à ce problème, Django construit une fonction justement nommée "recherche inversée", que vous pouvez utiliser. Le nom de la fonction est construit en mettant en minuscule le nom du modèle où a été déclarée la <code>ForeignKey</code>, suivi de <code>_set</code> (ainsi la fonction créée dans <code>Book</code> est <code>bookinstance_set()</code>).</p>
+
+<div class="note">
+<p><strong>Note </strong>: Ici nous utilisons <code>all()</code> pour récupérer tous les enregistrements (comportement par défaut). Bien que vous puissiez utiliser la méthode <code>filter()</code> pour obtenir un sous-ensemble d'enregistrements dans le code, vous ne pouvez faire cela directement dans le template, parce que vous ne pouvez pas spécifier d'arguments dans les fonctions.</p>
+
+<p>Prenez garde également que, si vous ne définissez pas un ordre (dans votre vue basée sur classe ou votre modèle), vous allez voir des erreurs de ce genre en provenance du serveur de développement :</p>
+
+<pre class="notranslate">[29/May/2017 18:37:53] "GET /catalog/books/?page=1 HTTP/1.1" 200 1637
+/foo/local_library/venv/lib/python3.5/site-packages/django/views/generic/list.py:99: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: &lt;QuerySet [&lt;Author: Ortiz, David&gt;, &lt;Author: H. McRaven, William&gt;, &lt;Author: Leigh, Melinda&gt;]&gt;
+ allow_empty_first_page=allow_empty_first_page, **kwargs)
+</pre>
+
+<p>Ceci vient du fait que l'<a href="https://docs.djangoproject.com/en/2.1/topics/pagination/#paginator-objects">objet paginator</a> s'attend à ce qu'un ORDER BY soit exécuté sur votre base de données sous-jacente. Sans cela il ne peut pas être sûr que les enregistrements retournés sont vraiment dans le bon ordre !</p>
+
+<p>Ce tutoriel n'a pas (encore !) traité de la <strong>pagination</strong>, mais comme vous ne pouvez pas utiliser <code>sort_by()</code> et passer un paramètre (pour la même raison que le <code>filter()</code> décrit précédemment), vous avez le choix entre trois options :</p>
+
+<ol>
+ <li>Ajouter un <code>ordering</code> lors de la déclaration de la <code>class Meta</code> dans votre modèle.</li>
+ <li>Ajouter un attribut <code>queryset</code> dans votre vue personnalisée basée sur classe, en spécifiant un <code>order_by()</code>.</li>
+ <li>Ajouter une méthode <code>get_queryset</code> à votre vue personnalisée basée sur classe, et préciser de même un <code>order_by()</code>.</li>
+</ol>
+
+<p>Si vous décidez d'ajouter une <code>class Meta</code> au modèle <code>Author</code> (solution peut-être pas aussi flexible que personnalier la vue basée sur classe, mais assez facile), vous allez vous retrouver avec quelque chose de ce genre :</p>
+
+<pre class="notranslate">class Author(models.Model):
+ first_name = models.CharField(max_length=100)
+ last_name = models.CharField(max_length=100)
+ date_of_birth = models.DateField(null=True, blank=True)
+ date_of_death = models.DateField('Died', null=True, blank=True)
+
+ def get_absolute_url(self):
+ return reverse('author-detail', args=[str(self.id)])
+
+ def __str__(self):
+ return f'{self.last_name}, {self.first_name}'
+
+<strong> class Meta:
+ ordering = ['last_name']</strong></pre>
+
+<p>Bien sûr le champ n'est pas forcément <code>last_name</code> : ce pourrait être un autre champ.</p>
+
+<p>Dernier point, mais non le moindre : vous devriez trier les données par un attribut/colonne qui a réellement un index (unique ou pas) dans votre base de données, afin d'éviter des problèmes de performance. Bien sûr ce n'est pas requis ici (ce serait un peu exagéré avec si peu de livres et d'utilisateurs), mais il vaut mieux avoir cela à l'esprit pour de futurs projets.</p>
+</div>
+
+<h2 id="À_quoi_cela_ressemble-t-il_2">À quoi cela ressemble-t-il ?</h2>
+
+<p>À ce point, nous devrions avoir créé tout ce qu'il faut pour afficher à la fois la liste des livres et les pages de détail pour chaque livre. Lancez le serveur (<code>python3 manage.py runserver</code>) et ouvrez votre navigateur à l'adresse <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>.</p>
+
+<div class="warning">
+<p><strong>Attention :</strong> Ne cliquez pas sur les liens vers le détail des auteurs : vous allez les créer lors du prochain défi !</p>
+</div>
+
+<p>Cliquez sur le lien <strong>Tous les livres</strong> pour afficher la liste des livres.</p>
+
+<p><img alt="Book List Page" src="https://mdn.mozillademos.org/files/14049/book_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 823px;"></p>
+
+<p>Ensuite cliquez sur un lien dirigeant vers l'un de vos livres. Si tout est réglé correctement, vous allez voir quelque chose de semblable à la capture d'écran suivante :</p>
+
+<p><img alt="Book Detail Page" src="https://mdn.mozillademos.org/files/14051/book_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 783px; margin: 0px auto; width: 926px;"></p>
+
+<h2 id="Pagination">Pagination</h2>
+
+<p>Si vous avez seulement quelques enregistrements, notre page de liste de livres aura une bonne apparence. Mais si vous avez des dizaines ou des centaines d'enregistrements, la page va progressivement devenir plus longue à charger (et aura beaucoup trop de contenu pour naviguer de manière raisonnable). La solution à ce problème est d'ajouter une pagination à vos vues listes, en réduisant le nombre d'éléments affichés sur chaque page.</p>
+
+<p>Django a d'excellents outils pour la pagination. Mieux encore, ces outils sont intégrés dans les vues listes génériques basées sur classes, aussi n'avez-vous pas grand chose à faire pour les activer !</p>
+
+<h3 id="Vues">Vues</h3>
+
+<p>Ouvrez <strong>catalog/views.py</strong>, et ajoutez la ligne <code>paginate_by</code>, en gras ci-dessous.</p>
+
+<pre class="brush: python notranslate">class BookListView(generic.ListView):
+ model = Book
+ <strong>paginate_by = 10</strong></pre>
+
+<p>Avec cet ajout, dès que vous aurez plus de 10 enregistrements, la vue démarrera la pagination des données qu'elle envoie au template. Les différentes pages sont obtenues en utilisant le paramètre GET : pour obtenir la page 2, vous utiliseriez l'URL <code>/catalog/books/<strong>?page=2</strong></code>.</p>
+
+<h3 id="Templates">Templates</h3>
+
+<p>Maintenant que les données sont paginées, nous avons besoin d'ajouter un outil au template pour parcourir l'ensemble des résultats. Et parce que nous voudrons sûrement faire cela pour toutes les listes vues, nous allons le faire d'une manière qui puisse être ajoutée au template de base.</p>
+
+<p>Ouvrez <strong>/locallibrary/catalog/templates/<em>base_generic.html</em></strong>, et copiez-y, sous notre bloc de contenu, le bloc de pagination suivant (mis en gras ci-dessous). Le code commence par vérifier si une pagination est activée sur la page courante. Si oui, il ajoute les liens "précédent" et "suivant" appropriés (et le numéro de la page courante).</p>
+
+<pre class="brush: python notranslate">{% block content %}{% endblock %}
+
+<strong>  {% block pagination %}
+    {% if is_paginated %}
+        &lt;div class="pagination"&gt;
+            &lt;span class="page-links"&gt;
+                {% if page_obj.has_previous %}
+                    &lt;a href="\{{ request.path }}?page=\{{ page_obj.previous_page_number }}"&gt;previous&lt;/a&gt;
+                {% endif %}
+                &lt;span class="page-current"&gt;
+                    Page \{{ page_obj.number }} of \{{ page_obj.paginator.num_pages }}.
+                &lt;/span&gt;
+                {% if page_obj.has_next %}
+                    &lt;a href="\{{ request.path }}?page=\{{ page_obj.next_page_number }}"&gt;next&lt;/a&gt;
+                {% endif %}
+            &lt;/span&gt;
+        &lt;/div&gt;
+    {% endif %}
+  {% endblock %} </strong></pre>
+
+<p>Le <code>page_obj</code> est un objet <a href="https://docs.djangoproject.com/en/2.1/topics/pagination/#paginator-objects">Paginator</a> qui n'existera que si une pagination est utilisée dans la page courante. Cet objet vous permet de récupérer toutes les informations sur la page courante, les pages précédentes, combien il y a de pages au total, etc.</p>
+
+<p>Nous utilisons <code>\{{ request.path }}</code> pour récupérer l'URL de la page courante, afin de créer les liens de pagination. Cela est utile, car cette variable est indépendante de l'objet que nous sommes en train de paginer.</p>
+
+<p>C'est tout !</p>
+
+<h3 id="À_quoi_cela_ressemble-t-il_3">À quoi cela ressemble-t-il ?</h3>
+
+<p>La capture d'écran ci-dessous montre à quoi ressemble la pagination. Si vous n'avez pas entré plus de 10 titres dans votre base de données, vous pouvez tester plus facilement cette pagination en diminuant le nombre spécifié à la ligne <code>paginate_by</code> dans votre  fichier <strong>catalog/views.py</strong>. Pour obtenir le résultat ci-dessous, nous avons changé la ligne en <code>paginate_by = 2</code>.</p>
+
+<p>Les liens de pagination sont affichés en bas de la page, avec les liens suivant/précédent affichés selon la page sur laquelle nous nous trouvons.</p>
+
+<p><img alt="Book List Page - paginated" src="https://mdn.mozillademos.org/files/14057/book_list_paginated.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 924px;"></p>
+
+<h2 id="Mettez-vous_vous-même_au_défi_!">Mettez-vous vous-même au défi !</h2>
+
+<p>Le challenge dans cet article consiste à créer les vues détail et liste nécessaires à l'achèvement du projet. Ces pages devront être accessibles aux URLs suivantes :</p>
+
+<ul>
+ <li><code>catalog/authors/</code> — La liste de tous les auteurs.</li>
+ <li><code>catalog/author/<em>&lt;id&gt;</em></code><em> </em>— La vue détail pour un auteur précis, avec un champ clé-primaire appelé <em><code>&lt;id&gt;</code></em>.</li>
+</ul>
+
+<p>Le code requis pour le mappeur d'URL et les vues sera virtuellement identique aux vues liste et détail du modèle <code>Book</code>, créées ci-dessus. Les templates seront différents, mais auront un comportement semblable.</p>
+
+<div class="note">
+<p><strong>Note</strong>:</p>
+
+<ul>
+ <li>Une fois que vous aurez créé le mappeur d'URL pour la page "liste d'auteurs", vous aurez besoin de mettre aussi à jour le lien <strong>All authors</strong> dans le template de base. Suivez la <a href="#Update_the_base_template">même procédure</a> que celle adoptée quand nous avons mis à jour le lien <strong>All books</strong>.</li>
+ <li>Une fois créé le mappeur d'URL pour la page de détails sur l'auteur, vous devrez aussi mettre à jour le <a href="#Creating_the_Detail_View_template">template de la vue détail d'un livre</a> (<strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>), de sorte que le lien vers l'auteur pointe vers votre nouvelle page de détails sur l'auteur (au lieu d'être une URL vide). La ligne va avoir comme changement la balise montrée en gras ci-dessous.
+ <pre class="brush: html notranslate">&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt;
+</pre>
+ </li>
+</ul>
+</div>
+
+<p>Quand vous aurez fini, vos pages vont ressembler aux captures d'écran suivantes.</p>
+
+<p><img alt="Author List Page" src="https://mdn.mozillademos.org/files/14053/author_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<ul>
+</ul>
+
+<p><img alt="Author Detail Page" src="https://mdn.mozillademos.org/files/14055/author_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 358px; margin: 0px auto; width: 825px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="Résumé">Résumé</h2>
+
+<p>Félicitations ! Notre application basique pour bibliothèque est maintenant terminée.</p>
+
+<p>Dans cet article, nous avons appris comment utiliser les vues génériques basées sur classe "liste" et "détail", et nous les avons utilisées pour créer des pages permettant de voir nos livres et nos auteurs. Au passage nous avons appris la recherche d'un pattern d'URL grâce aux expressions régulières, et la manière de passer des données depuis les URLs vers les vues. Nous avons aussi appris quelques trucs supplémentaires pour mieux utiliser les templates. Et en dernier nous vous avons montré comment paginer les vues liste, de façon à pouvoir gérer des listes même avec beaucoup d'enregistrements.</p>
+
+<p>Dans les articles que nous vous présenterons ensuite, nous améliorerons cette application pour intégrer des comptes utilisateurs, et nous allons donc vous montrer comment gérer l'authentification des utilisateurs, les permissions, les sessions et les formulaires.</p>
+
+<h2 id="Voyez_aussi">Voyez aussi</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-display/">Built-in class-based generic views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-display/">Generic display views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/topics/class-based-views/intro/">Introduction to class-based views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/ref/templates/builtins">Built-in template tags and filters</a> (Django docs).</li>
+ <li><a href="https://docs.djangoproject.com/en/2.1/topics/pagination/">Pagination</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</p>
+
+<h2 id="In_this_module">In this module</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>