From e26d24940b2234a1a5e63b19d19d298bf36354e2 Mon Sep 17 00:00:00 2001 From: julieng Date: Sun, 14 Nov 2021 14:30:32 +0100 Subject: move *.html to *.md --- .../learn/server-side/django/admin_site/index.html | 372 -------- .../learn/server-side/django/admin_site/index.md | 372 ++++++++ .../django/development_environment/index.html | 413 --------- .../django/development_environment/index.md | 413 +++++++++ files/fr/learn/server-side/django/forms/index.html | 651 -------------- files/fr/learn/server-side/django/forms/index.md | 651 ++++++++++++++ .../server-side/django/generic_views/index.html | 635 -------------- .../server-side/django/generic_views/index.md | 635 ++++++++++++++ .../learn/server-side/django/home_page/index.html | 429 --------- .../fr/learn/server-side/django/home_page/index.md | 429 +++++++++ files/fr/learn/server-side/django/index.html | 65 -- files/fr/learn/server-side/django/index.md | 65 ++ .../server-side/django/introduction/index.html | 277 ------ .../learn/server-side/django/introduction/index.md | 277 ++++++ .../fr/learn/server-side/django/models/index.html | 462 ---------- files/fr/learn/server-side/django/models/index.md | 462 ++++++++++ .../server-side/django/skeleton_website/index.html | 403 --------- .../server-side/django/skeleton_website/index.md | 403 +++++++++ .../fr/learn/server-side/django/testing/index.html | 956 --------------------- files/fr/learn/server-side/django/testing/index.md | 956 +++++++++++++++++++++ .../tutorial_local_library_website/index.html | 101 --- .../django/tutorial_local_library_website/index.md | 101 +++ .../fr/learn/server-side/express_nodejs/index.html | 66 -- files/fr/learn/server-side/express_nodejs/index.md | 66 ++ .../express_nodejs/introduction/index.html | 521 ----------- .../express_nodejs/introduction/index.md | 521 +++++++++++ .../first_steps/client-server_overview/index.html | 317 ------- .../first_steps/client-server_overview/index.md | 317 +++++++ files/fr/learn/server-side/first_steps/index.html | 46 - files/fr/learn/server-side/first_steps/index.md | 46 + .../first_steps/introduction/index.html | 235 ----- .../server-side/first_steps/introduction/index.md | 235 +++++ .../first_steps/web_frameworks/index.html | 307 ------- .../first_steps/web_frameworks/index.md | 307 +++++++ .../first_steps/website_security/index.html | 163 ---- .../first_steps/website_security/index.md | 163 ++++ files/fr/learn/server-side/index.html | 58 -- files/fr/learn/server-side/index.md | 58 ++ 38 files changed, 6477 insertions(+), 6477 deletions(-) delete mode 100644 files/fr/learn/server-side/django/admin_site/index.html create mode 100644 files/fr/learn/server-side/django/admin_site/index.md delete mode 100644 files/fr/learn/server-side/django/development_environment/index.html create mode 100644 files/fr/learn/server-side/django/development_environment/index.md delete mode 100644 files/fr/learn/server-side/django/forms/index.html create mode 100644 files/fr/learn/server-side/django/forms/index.md delete mode 100644 files/fr/learn/server-side/django/generic_views/index.html create mode 100644 files/fr/learn/server-side/django/generic_views/index.md delete mode 100644 files/fr/learn/server-side/django/home_page/index.html create mode 100644 files/fr/learn/server-side/django/home_page/index.md delete mode 100644 files/fr/learn/server-side/django/index.html create mode 100644 files/fr/learn/server-side/django/index.md delete mode 100644 files/fr/learn/server-side/django/introduction/index.html create mode 100644 files/fr/learn/server-side/django/introduction/index.md delete mode 100644 files/fr/learn/server-side/django/models/index.html create mode 100644 files/fr/learn/server-side/django/models/index.md delete mode 100644 files/fr/learn/server-side/django/skeleton_website/index.html create mode 100644 files/fr/learn/server-side/django/skeleton_website/index.md delete mode 100644 files/fr/learn/server-side/django/testing/index.html create mode 100644 files/fr/learn/server-side/django/testing/index.md delete mode 100644 files/fr/learn/server-side/django/tutorial_local_library_website/index.html create mode 100644 files/fr/learn/server-side/django/tutorial_local_library_website/index.md delete mode 100644 files/fr/learn/server-side/express_nodejs/index.html create mode 100644 files/fr/learn/server-side/express_nodejs/index.md delete mode 100644 files/fr/learn/server-side/express_nodejs/introduction/index.html create mode 100644 files/fr/learn/server-side/express_nodejs/introduction/index.md delete mode 100644 files/fr/learn/server-side/first_steps/client-server_overview/index.html create mode 100644 files/fr/learn/server-side/first_steps/client-server_overview/index.md delete mode 100644 files/fr/learn/server-side/first_steps/index.html create mode 100644 files/fr/learn/server-side/first_steps/index.md delete mode 100644 files/fr/learn/server-side/first_steps/introduction/index.html create mode 100644 files/fr/learn/server-side/first_steps/introduction/index.md delete mode 100644 files/fr/learn/server-side/first_steps/web_frameworks/index.html create mode 100644 files/fr/learn/server-side/first_steps/web_frameworks/index.md delete mode 100644 files/fr/learn/server-side/first_steps/website_security/index.html create mode 100644 files/fr/learn/server-side/first_steps/website_security/index.md delete mode 100644 files/fr/learn/server-side/index.html create mode 100644 files/fr/learn/server-side/index.md (limited to 'files/fr/learn/server-side') diff --git a/files/fr/learn/server-side/django/admin_site/index.html b/files/fr/learn/server-side/django/admin_site/index.html deleted file mode 100644 index af9b0309f9..0000000000 --- a/files/fr/learn/server-side/django/admin_site/index.html +++ /dev/null @@ -1,372 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}
- -

Nous avons créé le modèle de données pour le site web de la bibliothèque locale. 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.

- - - - - - - - - - - - -
Pré-requis:Avoir complété Django didactique Section 3: Utilisation des modèles de données
Objectif: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.
- -

Survol

- -

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).

- -

Quand nous avons créé le squelette du projet, nous avons généré automatiquement toute ce qui était nécessaire à son administration au sein de l'application web (le détail des relations en jeux 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.

- -

Passons aux actes ! Après l'enregistrement des objets du modèle relationnel, nous verrons comment créer un super-utilisateur, s'authentifier et ensuite créer quelques livres, auteurs et ouvrages à la disposition des lecteurs. Ces données seront très utiles pour tester ensuite les vues et gabarits qui seront abordés dans les chapitres suivants.

- -

Enregistrer les objets de la modélisation

- -

En premier lieu, il faut editer le fichier admin.py de l'application catalog (c'est-à-dire le fichier ./locallibrary/catalog/admin.py). Il devrait ressembler à celui ci-dessous — notez qu'il contient d'ores et déjà l'import du module django.contrib.admin:

- -
from django.contrib import admin
-
-# Register your models here.
-
- -

L'enregistrement de objets de modélisation se fait par l'appel de la fonction admin.site.register 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.

- -
from catalog.models import Author, Genre, Book, BookInstance
-
-admin.site.register(Book)
-admin.site.register(Author)
-admin.site.register(Genre)
-admin.site.register(BookInstance)
-
- -
-

Note : Si vous avez répondu au défi de la modelisation des langues des livres (voir le chapitre précédent sur les modèles de données), vous pouvez aussi importer cet objet !

-

Cela devrait être de la forme : admin.site.register(Language) et n'oubliez pas d'importer l'objet.

-
- -

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.

- -

Générer un super-utilisateur

- -

Pour acceder au site d'administration, il est necessaire de s'authentifier avec un utilisateur qui dispose du statut Statut d'équipe 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 manage.py un super-utilisateur qui dispose de tous les droits et permissions.

- -

Exécutez la commande python ci-dessous qui appelle le fichier manage.py en étant dans le même dossier que le fichier (c'est-à-dire ./locallibrary/), 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.

- -
python3 manage.py createsuperuser
-
- -

Une fois cette étape réalisée, vous pouvez redémarrer le serveur de développement :

- -
python3 manage.py runserver
-
-
- -

Accéder et utiliser le site admin

- -

Pour vous authentifier au site, ouvrez l'URL /admin du site local (concrètement, http://127.0.0.1:8000/admin) et identifiez vous avec votre compte de super-utilisateur.

- -
-

Note : Vous serez redirigé⋅e 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.

- -

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...

-
- -

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 + Ajouter pour créer un nouvel enregistrement.

- -

Admin Site - Home page

- -

Cliquez sur le lien + Ajouter à 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 help_text quand vous l'aviez défini. 

- -

Entrez les valeurs des champs. Pour les champs qui relèvent de relations entre objet, vous pouvez utiliser le bouton + pour accéder en cascade au formulaire 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 Enregistrer et ajouter un nouveau ou Enregistrer et continuer les modification pour sauvegarder en base de données les informations saisies.

- -

Admin Site - Book Add

- -
-

Note : À 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.

-
- -

Après avoir saisie les informations et ajouté vos livres, cliquez sur le lien Accueil pour revenir à la page principale du site d'administration. Cliquez sur le lien Books 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 __str__() du modèle d'objet Book comme cela a été codé dans le précédent chapitre.

- -

Admin Site - List of book objects

- -

À partir de la liste affichée, vous pouvez supprimer des instances en selectionnant les items par les cases à cocher à gauche du titre puis supprimer... dans la liste des actions proposée puis en cliquant sur Envoyer. Vous pouvez aussi ajouter des livres en cliquant sur AJOUTER BOOK.

- -

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 get_absolute_url() dans la définition du modèle de données (à ce stade, une erreur sera provoquée si vous cliquez sur ce bouton).

- -

Admin Site - Book Edit

- -

Revenez à la page d'accueil (à l'aide du lien Accueil du fil d'Ariane), puis affichez les listes des Authors et des Genres. 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.

- -

Ce qui manque actuellement ce sont des Book Instances. Vous n'en avez pas car elles ne sont pas créées à partir des objets Books (bien que vous pourriez créer un objet Book à partir d'un objet BookInstance car c'est la nature de la relation ForeignKey). Retournez à la page d'acceuil et cliquez sur le bouton Ajouter 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.

- -

Admin Site - BookInstance Add

- -

Créez plusieurs de ces enregistrements pour chacun de vos livres. Définissez un statut Available (Disponible) pour certains d'entre eux et On loan (Prêt) pour d’autres. Pour un statut différent de Available, vous devrez préciser une date d'échéance à venir.

- -

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.

- -

Configuration avancée

- -

La cadriciel Django réalise une excellente assistance avec la création d'un site d'administration de base en utilisant les données des enregistrements effectués :

- - - -

mais vous avez la possibilité de personnaliser le comportement du site d'administration. Vous allez pouvoir notamment faire :

- - - -

Dans la section qui suit, nous allons effectuer quelques modification pour améliorer l'interface de votre application LocalLibrary. Nous allons notamment ajouter des informations pour les objets Book et Author, et améliorer la présentation de leur vue d'édition. Il n'y aura pas de changement pour les objets Language et Genre qui ne possèdent pas assez d'information pour que cela puisse avoir une incidence réelle !

- -

Le détail complet de la personnalisation du site d'administration est disponible sur le site documentaire de Django.

- -

Enregistrer un objet de la classe ModelAdmin

- -

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.

- -

Commençons avec l'objet Author. Éditez le fichier admin.py dans le dossier catalog de l'application(concrètement le fichier /locallibrary/catalog/admin.py). Commentez la ligne qui vous a permis d'enregistrer l'objet Author :

- -
# admin.site.register(Author)
- -

Ensuite ajoutez une nouvelle classe d'objets AuthorAdmin et enregistrez-le comme indiqué ci-dessous.

- -
# Define the admin class
-class AuthorAdmin(admin.ModelAdmin):
-    pass
-
-# Register the admin class with the associated model
-admin.site.register(Author, AuthorAdmin)
-
- -

Ensuite nous allons opérer de manière analogue avec un objet hérité de ModelAdmin pour les objets Book, et BookInstance. À nouveau, nous commentons les enregistrements initiaux :

- -
# admin.site.register(Book)
-# admin.site.register(BookInstance)
- -

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 @register qui réalise la même opération que la méthode admin.site.register() :

- -
# 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
-
- -

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 nouvellement enregistrés.

- -

Configurer les vues en liste

- -

La liste des auteurs (objet Author) est affichée dans l'application LocalLibrary à l'aide du nom généré par la méthode  __str__(). 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'information, vous allez utiliser la directive list_display pour ajouter d'autres champs de l'objet Author.

- -

Modifiez votre classe AuthorAdmin comme décrit ci-dessous (vous pouvez copier et coller le code). Les noms de champs à afficher dans la liste sont déclarés dans un tuple dans l'ordre requis. Ils sont identiques à ceux spécifiés dans votre modèle d'objet Author.

- -
class AuthorAdmin(admin.ModelAdmin):
-    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
-
- -

Si vous accèdez à la page d'administration des auteurs, vous devriez obtenir une page équivalente à celle ci-dessous :

- -

Admin Site - Improved Author List

- -

Pour les livres, nous allons visulaiser les objets  Book en affichant les champs author and genre. Le champs author est de type ForeignKey décrivant une relation un à n. En conséquence, nous afficherons l'élément produit par la méthode __str__() de l'objet Author 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 BookAdmin comme suit :

- -
class BookAdmin(admin.ModelAdmin):
-    list_display = ('title', 'author', 'display_genre')
-
- -

Le champ genre représente une relation n à n (ManyToManyField) qui ne peut pas être prise en charge par la directive list_display. 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(display_genre) qui permettra de traiter l'affichage des informations souhaitées pour le genre.

- -
-

Note : C'est dans un but pédagogique que nous recherchons ici l'affichage du genre 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.

-
- -

Ajoutez le code ci-dessous dans votre modèle d'objet Book (concrètement dans le fichier locallibrary/catalog/models.py). 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 (short_description) qui sera utilisé par le site d'administration avec cette méthode.

- -
    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'
-
- -

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 :

- -

Admin Site - Improved Book List

- -

Les champs Genre Language ne dispose que d'une seule valeur. Il n'est donc pas utile de créer une page d'affichage spélicale.

- -
-

Note : Vous trouverez en fin d'article dans la défis personnel des propositions pour améliorer les ouvrages en prêt BookInstance !

-
- -

Ajouter des filtres

- -

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 list_filter de la classe ModelAdmin. Modifier votre classe d'objet d'affichage BookInstanceAdmin avec les code ci-dessous :

- -
class BookInstanceAdmin(admin.ModelAdmin):
-    list_filter = ('status', 'due_back')
-
- -

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).

- -

Admin Site - BookInstance List Filters

- -

Organiser la vue d'affichage d'un modèle

- -

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.

- -
-

Note : Les modèles de l'application LocalLibrary 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.

-
- -

Contrôler l'affichage et la dispostion des champs

- -

Modifiez votre classe d'objet AuthorAdmin en ajoutant l'attribut fields comme indiqué en gras ci-dessous :

- -
class AuthorAdmin(admin.ModelAdmin):
-    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
-    fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]
-
- -

Ce champ (fields) 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).

- -

La page web de votre application locale devrait ressembler à celle ci-dessous :

- -

Admin Site - Improved Author Detail

- -
-

Note : Vous pouvez aussi utiliser l'attribut exclude 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 exclude.

-
- -

Organiser des sections dans votre vue de détail

- -

Vous avez la possibilité de créer des sections à l'affichage pour regrouper des éléments à renseigner en utilisant l'attribut fieldsets.

- -

Nous allons utiliser l'objet BookInstance 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 :

- -
@admin.register(BookInstance)
-class BookInstanceAdmin(admin.ModelAdmin):
-    list_filter = ('status', 'due_back')
-
-    fieldsets = (
-        (None, {
-            'fields': ('book', 'imprint', 'id')
-        }),
-        ('Availability', {
-            'fields': ('status', 'due_back')
-        }),
-    )
- -

Chaque section peut avoir un titre (ou aucun si vous indiquez la valeur None) 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.

- -

Le résultat de cette description devrait vous apparaître de manière analogue à celle présente  ci-dessous :

- -

Admin Site - Improved BookInstance Detail with sections

- -

Publier des enregistrements associés

- -

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..

- -

Pour cela, vous pouvez utiliser un d'objet pour un affichage horizontal (TabularInline) ou vertical (StackedInline) (qui n'est autre que l'affichage standard des données). Modifiez le code associé à votre modèle BookInstance dans le fichier admin.py pour disposer des informations inline à l'affichage des informations sur votre objet Book. Gardez en mémoire que c'est l'objet  BookAdmin qui gère l'affichage les informations de l'objet Book; c'est donc BookAdmin il doit donc être modifié :

- -
class BooksInstanceInline(admin.TabularInline):
-    model = BookInstance
-
-@admin.register(Book)
-class BookAdmin(admin.ModelAdmin):
-    list_display = ('title', 'author', 'display_genre')
-    inlines = [BooksInstanceInline]
-
- -

Si vous allez consulter un livre, vous devriez pouvoir, au bas de la page, consulter la liste des copies enregistrées :

- -

Admin Site - Book with Inlines

- -

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 TabularInline vous aurez accès à l'ensemble des éléments qui permettent de filtrer et afficher les éléments dont vous aurez besoin. 

- -
-

Note : Il y a quelques limitations 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 extra à 0 dans la définition de l'objet BooksInstanceInline ... essayez !

-
- -

Défi

- -

Beaucoup de sujets ont été abordés dans ce chapitre, c'est l'occasion de les mettre en application :

- -
    -
  1. Améliorer l'affichage des objets BookInstance, 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 __str__() de l'objet.
  2. -
  3. Ajouter une information associée pour disposer du détail des informations sur l'auteur. Appuyez vous sur l'exemple avec les objets Book/BookInstance pour y parvenir.
  4. -
- - - -

Résumé

- -

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.

- -

A voir aussi

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}

- -

In this module

- - diff --git a/files/fr/learn/server-side/django/admin_site/index.md b/files/fr/learn/server-side/django/admin_site/index.md new file mode 100644 index 0000000000..af9b0309f9 --- /dev/null +++ b/files/fr/learn/server-side/django/admin_site/index.md @@ -0,0 +1,372 @@ +--- +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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}
+ +

Nous avons créé le modèle de données pour le site web de la bibliothèque locale. 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.

+ + + + + + + + + + + + +
Pré-requis:Avoir complété Django didactique Section 3: Utilisation des modèles de données
Objectif: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.
+ +

Survol

+ +

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).

+ +

Quand nous avons créé le squelette du projet, nous avons généré automatiquement toute ce qui était nécessaire à son administration au sein de l'application web (le détail des relations en jeux 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.

+ +

Passons aux actes ! Après l'enregistrement des objets du modèle relationnel, nous verrons comment créer un super-utilisateur, s'authentifier et ensuite créer quelques livres, auteurs et ouvrages à la disposition des lecteurs. Ces données seront très utiles pour tester ensuite les vues et gabarits qui seront abordés dans les chapitres suivants.

+ +

Enregistrer les objets de la modélisation

+ +

En premier lieu, il faut editer le fichier admin.py de l'application catalog (c'est-à-dire le fichier ./locallibrary/catalog/admin.py). Il devrait ressembler à celui ci-dessous — notez qu'il contient d'ores et déjà l'import du module django.contrib.admin:

+ +
from django.contrib import admin
+
+# Register your models here.
+
+ +

L'enregistrement de objets de modélisation se fait par l'appel de la fonction admin.site.register 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.

+ +
from catalog.models import Author, Genre, Book, BookInstance
+
+admin.site.register(Book)
+admin.site.register(Author)
+admin.site.register(Genre)
+admin.site.register(BookInstance)
+
+ +
+

Note : Si vous avez répondu au défi de la modelisation des langues des livres (voir le chapitre précédent sur les modèles de données), vous pouvez aussi importer cet objet !

+

Cela devrait être de la forme : admin.site.register(Language) et n'oubliez pas d'importer l'objet.

+
+ +

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.

+ +

Générer un super-utilisateur

+ +

Pour acceder au site d'administration, il est necessaire de s'authentifier avec un utilisateur qui dispose du statut Statut d'équipe 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 manage.py un super-utilisateur qui dispose de tous les droits et permissions.

+ +

Exécutez la commande python ci-dessous qui appelle le fichier manage.py en étant dans le même dossier que le fichier (c'est-à-dire ./locallibrary/), 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.

+ +
python3 manage.py createsuperuser
+
+ +

Une fois cette étape réalisée, vous pouvez redémarrer le serveur de développement :

+ +
python3 manage.py runserver
+
+
+ +

Accéder et utiliser le site admin

+ +

Pour vous authentifier au site, ouvrez l'URL /admin du site local (concrètement, http://127.0.0.1:8000/admin) et identifiez vous avec votre compte de super-utilisateur.

+ +
+

Note : Vous serez redirigé⋅e 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.

+ +

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...

+
+ +

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 + Ajouter pour créer un nouvel enregistrement.

+ +

Admin Site - Home page

+ +

Cliquez sur le lien + Ajouter à 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 help_text quand vous l'aviez défini. 

+ +

Entrez les valeurs des champs. Pour les champs qui relèvent de relations entre objet, vous pouvez utiliser le bouton + pour accéder en cascade au formulaire 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 Enregistrer et ajouter un nouveau ou Enregistrer et continuer les modification pour sauvegarder en base de données les informations saisies.

+ +

Admin Site - Book Add

+ +
+

Note : À 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.

+
+ +

Après avoir saisie les informations et ajouté vos livres, cliquez sur le lien Accueil pour revenir à la page principale du site d'administration. Cliquez sur le lien Books 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 __str__() du modèle d'objet Book comme cela a été codé dans le précédent chapitre.

+ +

Admin Site - List of book objects

+ +

À partir de la liste affichée, vous pouvez supprimer des instances en selectionnant les items par les cases à cocher à gauche du titre puis supprimer... dans la liste des actions proposée puis en cliquant sur Envoyer. Vous pouvez aussi ajouter des livres en cliquant sur AJOUTER BOOK.

+ +

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 get_absolute_url() dans la définition du modèle de données (à ce stade, une erreur sera provoquée si vous cliquez sur ce bouton).

+ +

Admin Site - Book Edit

+ +

Revenez à la page d'accueil (à l'aide du lien Accueil du fil d'Ariane), puis affichez les listes des Authors et des Genres. 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.

+ +

Ce qui manque actuellement ce sont des Book Instances. Vous n'en avez pas car elles ne sont pas créées à partir des objets Books (bien que vous pourriez créer un objet Book à partir d'un objet BookInstance car c'est la nature de la relation ForeignKey). Retournez à la page d'acceuil et cliquez sur le bouton Ajouter 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.

+ +

Admin Site - BookInstance Add

+ +

Créez plusieurs de ces enregistrements pour chacun de vos livres. Définissez un statut Available (Disponible) pour certains d'entre eux et On loan (Prêt) pour d’autres. Pour un statut différent de Available, vous devrez préciser une date d'échéance à venir.

+ +

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.

+ +

Configuration avancée

+ +

La cadriciel Django réalise une excellente assistance avec la création d'un site d'administration de base en utilisant les données des enregistrements effectués :

+ + + +

mais vous avez la possibilité de personnaliser le comportement du site d'administration. Vous allez pouvoir notamment faire :

+ + + +

Dans la section qui suit, nous allons effectuer quelques modification pour améliorer l'interface de votre application LocalLibrary. Nous allons notamment ajouter des informations pour les objets Book et Author, et améliorer la présentation de leur vue d'édition. Il n'y aura pas de changement pour les objets Language et Genre qui ne possèdent pas assez d'information pour que cela puisse avoir une incidence réelle !

+ +

Le détail complet de la personnalisation du site d'administration est disponible sur le site documentaire de Django.

+ +

Enregistrer un objet de la classe ModelAdmin

+ +

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.

+ +

Commençons avec l'objet Author. Éditez le fichier admin.py dans le dossier catalog de l'application(concrètement le fichier /locallibrary/catalog/admin.py). Commentez la ligne qui vous a permis d'enregistrer l'objet Author :

+ +
# admin.site.register(Author)
+ +

Ensuite ajoutez une nouvelle classe d'objets AuthorAdmin et enregistrez-le comme indiqué ci-dessous.

+ +
# Define the admin class
+class AuthorAdmin(admin.ModelAdmin):
+    pass
+
+# Register the admin class with the associated model
+admin.site.register(Author, AuthorAdmin)
+
+ +

Ensuite nous allons opérer de manière analogue avec un objet hérité de ModelAdmin pour les objets Book, et BookInstance. À nouveau, nous commentons les enregistrements initiaux :

+ +
# admin.site.register(Book)
+# admin.site.register(BookInstance)
+ +

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 @register qui réalise la même opération que la méthode admin.site.register() :

+ +
# 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
+
+ +

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 nouvellement enregistrés.

+ +

Configurer les vues en liste

+ +

La liste des auteurs (objet Author) est affichée dans l'application LocalLibrary à l'aide du nom généré par la méthode  __str__(). 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'information, vous allez utiliser la directive list_display pour ajouter d'autres champs de l'objet Author.

+ +

Modifiez votre classe AuthorAdmin comme décrit ci-dessous (vous pouvez copier et coller le code). Les noms de champs à afficher dans la liste sont déclarés dans un tuple dans l'ordre requis. Ils sont identiques à ceux spécifiés dans votre modèle d'objet Author.

+ +
class AuthorAdmin(admin.ModelAdmin):
+    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+
+ +

Si vous accèdez à la page d'administration des auteurs, vous devriez obtenir une page équivalente à celle ci-dessous :

+ +

Admin Site - Improved Author List

+ +

Pour les livres, nous allons visulaiser les objets  Book en affichant les champs author and genre. Le champs author est de type ForeignKey décrivant une relation un à n. En conséquence, nous afficherons l'élément produit par la méthode __str__() de l'objet Author 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 BookAdmin comme suit :

+ +
class BookAdmin(admin.ModelAdmin):
+    list_display = ('title', 'author', 'display_genre')
+
+ +

Le champ genre représente une relation n à n (ManyToManyField) qui ne peut pas être prise en charge par la directive list_display. 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(display_genre) qui permettra de traiter l'affichage des informations souhaitées pour le genre.

+ +
+

Note : C'est dans un but pédagogique que nous recherchons ici l'affichage du genre 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.

+
+ +

Ajoutez le code ci-dessous dans votre modèle d'objet Book (concrètement dans le fichier locallibrary/catalog/models.py). 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 (short_description) qui sera utilisé par le site d'administration avec cette méthode.

+ +
    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'
+
+ +

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 :

+ +

Admin Site - Improved Book List

+ +

Les champs Genre Language ne dispose que d'une seule valeur. Il n'est donc pas utile de créer une page d'affichage spélicale.

+ +
+

Note : Vous trouverez en fin d'article dans la défis personnel des propositions pour améliorer les ouvrages en prêt BookInstance !

+
+ +

Ajouter des filtres

+ +

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 list_filter de la classe ModelAdmin. Modifier votre classe d'objet d'affichage BookInstanceAdmin avec les code ci-dessous :

+ +
class BookInstanceAdmin(admin.ModelAdmin):
+    list_filter = ('status', 'due_back')
+
+ +

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).

+ +

Admin Site - BookInstance List Filters

+ +

Organiser la vue d'affichage d'un modèle

+ +

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.

+ +
+

Note : Les modèles de l'application LocalLibrary 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.

+
+ +

Contrôler l'affichage et la dispostion des champs

+ +

Modifiez votre classe d'objet AuthorAdmin en ajoutant l'attribut fields comme indiqué en gras ci-dessous :

+ +
class AuthorAdmin(admin.ModelAdmin):
+    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+    fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]
+
+ +

Ce champ (fields) 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).

+ +

La page web de votre application locale devrait ressembler à celle ci-dessous :

+ +

Admin Site - Improved Author Detail

+ +
+

Note : Vous pouvez aussi utiliser l'attribut exclude 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 exclude.

+
+ +

Organiser des sections dans votre vue de détail

+ +

Vous avez la possibilité de créer des sections à l'affichage pour regrouper des éléments à renseigner en utilisant l'attribut fieldsets.

+ +

Nous allons utiliser l'objet BookInstance 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 :

+ +
@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+    list_filter = ('status', 'due_back')
+
+    fieldsets = (
+        (None, {
+            'fields': ('book', 'imprint', 'id')
+        }),
+        ('Availability', {
+            'fields': ('status', 'due_back')
+        }),
+    )
+ +

Chaque section peut avoir un titre (ou aucun si vous indiquez la valeur None) 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.

+ +

Le résultat de cette description devrait vous apparaître de manière analogue à celle présente  ci-dessous :

+ +

Admin Site - Improved BookInstance Detail with sections

+ +

Publier des enregistrements associés

+ +

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..

+ +

Pour cela, vous pouvez utiliser un d'objet pour un affichage horizontal (TabularInline) ou vertical (StackedInline) (qui n'est autre que l'affichage standard des données). Modifiez le code associé à votre modèle BookInstance dans le fichier admin.py pour disposer des informations inline à l'affichage des informations sur votre objet Book. Gardez en mémoire que c'est l'objet  BookAdmin qui gère l'affichage les informations de l'objet Book; c'est donc BookAdmin il doit donc être modifié :

+ +
class BooksInstanceInline(admin.TabularInline):
+    model = BookInstance
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+    list_display = ('title', 'author', 'display_genre')
+    inlines = [BooksInstanceInline]
+
+ +

Si vous allez consulter un livre, vous devriez pouvoir, au bas de la page, consulter la liste des copies enregistrées :

+ +

Admin Site - Book with Inlines

+ +

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 TabularInline vous aurez accès à l'ensemble des éléments qui permettent de filtrer et afficher les éléments dont vous aurez besoin. 

+ +
+

Note : Il y a quelques limitations 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 extra à 0 dans la définition de l'objet BooksInstanceInline ... essayez !

+
+ +

Défi

+ +

Beaucoup de sujets ont été abordés dans ce chapitre, c'est l'occasion de les mettre en application :

+ +
    +
  1. Améliorer l'affichage des objets BookInstance, 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 __str__() de l'objet.
  2. +
  3. Ajouter une information associée pour disposer du détail des informations sur l'auteur. Appuyez vous sur l'exemple avec les objets Book/BookInstance pour y parvenir.
  4. +
+ + + +

Résumé

+ +

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.

+ +

A voir aussi

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}

+ +

In this module

+ + diff --git a/files/fr/learn/server-side/django/development_environment/index.html b/files/fr/learn/server-side/django/development_environment/index.html deleted file mode 100644 index 326bce1716..0000000000 --- a/files/fr/learn/server-side/django/development_environment/index.html +++ /dev/null @@ -1,413 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}
- -

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.

- - - - - - - - - - - - -
Prérequis :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.
Objectif :Avoir un environnement de développement pour Django (2.0) fonctionnel sur votre ordinateur.
- -

Aperçu de l'environnement de développement Django

- -

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 recommandée pour installer l'environnement de développement Django sur Ubuntu, macOS et Windows, et comment le tester.

- -

Qu'est-ce que l'environnement de développement Django ?

- -

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.

- -

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 serveur web de développement 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.

- -

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 éditeur de texte 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é.

- -

Quelles sont les options d'installation de Django ?

- -

Django est extrêmement flexible sur sa manière d'être installé et configuré. Django peut-être :

- - - -

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.

- -
-

Note : D'autres options d'installation possibles sont traitées dans la documentation officielle de Django. Les liens vers la documentation appropriée peuvent-être trouvés ci-dessous.

-
- -

Quels systèmes d'exploitation sont supportés ?

- -

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.

- -

Dans cet article, nous vous donnons les instructions pour Windows, macOS et Linux/Unix.

- -

Quelle version de Python doit-être utilisée ?

- -

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.

- -

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).

- -
-

Note : 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).

-
- -

Où peut-on télécharger Django ?

- -

Il y a trois façons de télécharger Django :

- - - -

Cet article explique comment installer Django depuis PyPI afin d'obtenir la version stable la plus récente.

- -

Quelle base de données ?

- -

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 problèmes potentiels qu'il vaut mieux éviter).

- -

Dans cet article (et quasiment tout le module), nous utiliserons la base SQLite, 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.

- -
-

Note : Django est configuré pour utiliser SQLite par défaut lorsque vous démarrez le projet de votre site web en utilisant les outils standards (django-admin). C'est un très bon choix lorsque vous débutez car elle ne requiert aucune configuration ou installation particulière.

-
- -

Installation globale ou dans un environnement virtuel Python ?

- -

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.

- -
-

Note : 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).

-
- -

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.

- -

Ainsi, un développeur Python/Django confirmé lance généralement ses applications Python dans des environnements virtuels Python 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.

- -

Ce module suppose que vous avez installé Django dans un environnement virtuel, et nous vous montrons comment le faire ci-dessous.

- -

Installer Python 3

- -

Si vous souhaitez utiliser Django, vous devrez installer Python sur votre système d'exploitation. Si vous utilisez Python 3, vous aurez alors aussi besoin de l'outil Python Package Indexpip3 — 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.

- -

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.

- -
-

Note : 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 https://www.python.org/downloads/ et les installer en utilisant la méthode appropriée à votre plateforme.

-
- -

Ubuntu 18.04

- -

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 :

- -
python3 -V
- Python 3.6.6
- -

Toutefois, l'outil d'Index des Packages Python dont vous aurez besoin pour installer des packages avec Python 3 (y compris Django) n'est pas disponible par défaut. Vous pouvez installer pip3 avec le terminal bash avec :

- -
sudo apt install python3-pip
- -

macOS

- -

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 :

- -
python3 -V
- -bash: python3ommand not found
- -

Vous pouvez facilement installer Python 3 (ainsi que l'outil pip3) sur python.org:

- -
    -
  1. Téléchargez l'installeur requis : -
      -
    1. Allez sur https://www.python.org/downloads/
    2. -
    3. Sélectionnez le bouton Download Python 3.7.2 (le numéro de version mineure peut varier).
    4. -
    -
  2. -
  3. Localisez le fichier en utilisant le Finder, puis double-cliquez le fichier package. Suivez les consignes d'installation.
  4. -
- -

Vous pouvez désormais confirmer la bonne installation en vérifiant votre version de Python 3 comme indiqué ci-dessous :

- -
python3 -V
- Python 3.7.2
-
- -

Vous pouvez aussi vérifier que pip3 est correctement installé en listant les packages disponibles :

- -
pip3 list
- -

Windows 10

- -

Windows n'inclut pas Python par défaut, mais vous pouvez facilement l'installer (ainsi que l'outil pip3) sur python.org:

- -
    -
  1. Téléchargez l'installeur requis : -
      -
    1. Allez sur https://www.python.org/downloads/
    2. -
    3. Sélectionnez le bouton Download Python 3.7.2 (le numéro de version mineure peut varier).
    4. -
    -
  2. -
  3. Installez Python en double-cliquant sur le fichier télécharger puis en suivant les consignes d'installation
  4. -
  5. Assurez-vous d'avoir coché la case intitulée "Ajouter Python au PATH".
  6. -
- -

Vous pouvez ensuite vérifier que Python s'est correctement installé en tapant le texte suivant dans votre invite de commande :

- -
py -3 -V
- Python 3.7.2
-
- -

L'installeur Windows inclut pip3 (le gestionnaire de packages Python) par défaut. Vous pouvez lister les packages installés de la manière suivante :

- -
pip3 list
-
- -
-

Note : 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.

-
- -

Utiliser Django dans un environnement virtuel Python

- -

Les librairies que nous utiliserons pour créer nos environnements virtuels seront virtualenvwrapper (Linux et macOS) et virtualenvwrapper-win (Windows), , qui à leur tour utilisent l'outil virtualenv. Les outils d'habillage permettent de créer une interface consistante pour gérer les interfaces sur toutes les plateformes.

- -

Installer l'utilitaire d'environnement virtuel

- -

Mise en place de l'environnement virtuel sur Ubuntu

- -

Après avoir installé Python et pip vous pouvez installer virtualenvwrapper (qui inclut virtualenv). Le guide d'installation officiel peut être trouvé ici, ou bien vous pouvez suivre les instructions ci-dessous.

- -

Installer l'outil en utilisant pip3:

- -
sudo pip3 install virtualenvwrapper
- -

Ajoutez ensuite les lignes suivantes à la fin de votre fichier de configuration shell (le fichier caché .bashrc 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 :

- -
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
- -
-

Note : Les variables VIRTUALENVWRAPPER_PYTHON et VIRTUALENVWRAPPER_VIRTUALENV_ARGS pointent vers l'emplacement d'installation par défaut de Python3, et source /usr/local/bin/virtualenvwrapper.sh pointe vers l'emplacement par défaut du script virtualenvwrapper.sh. Si le virtualenv 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).
-
- Vous pourrez trouver les bons emplacements de votre système en utilisant les commandes which virtualenvwrapper.sh et which python3.

-
- -

Puis relancez le fichier de configuration en exécutant la commande suivante dans votre terminal :

- -
source ~/.bashrc
- -

Vous devriez alors voir apparaître plusieurs lignes de script semblables à celles ci-dessous :

- -
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
- -

Vous pouvez maintenant créer un nouvel environnement virtuel avec la commande mkvirtualenv.

- -

Mise en place de l'environnement virtuel sur macOS

- -

L'installation de virtualenvwrapper on sur macOS est quasiment identique à celle sur Ubuntu (une fois de plus, vous pouvez suivre les instructions du guide officiel d'installation ou suivre les indications ci-dessous).

- -

Installez virtualenvwrapper (ainsi que virtualenv) en utilisant pip comme indiqué ci-dessous.

- -
sudo pip3 install virtualenvwrapper
- -

Puis ajoutez les lignes suivantes à la fin de votre fichier de configuration shell.

- -
export WORKON_HOME=$HOME/.virtualenvs
-export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
-export PROJECT_HOME=$HOME/Devel
-source /usr/local/bin/virtualenvwrapper.sh
- -
-

Note : La variable VIRTUALENVWRAPPER_PYTHON pointe vers l'emplacement d'installation par défaut de Python3, et source /usr/local/bin/virtualenvwrapper.sh pointe vers l'emplacement par défaut du script virtualenvwrapper.sh. Si le virtualenv 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).

- -

Par exemple, une installation test sur macOS a résulté en l'ajout des lignes suivantes dans le fichier startup :

- -
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
- -

Vous pourrez trouver les bons emplacements de votre système en utilisant les commandes which virtualenvwrapper.sh et which python3.

-
- -

Ce sont les mêmes lignes que pour Ubuntu, mais le nom du fichier de configuration caché du répertoire home est différent : .bash_profile dans votre répertoire home.

- -
-

Note : Si vous n'arrivez pas à trouver le fichier .bash_profile depuis le finder, vous pouvez aussi l'ouvrir depuis le terminal en utilisant nano.

- -

La commande sera la suivante :

- -
>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.
-
- -

Puis relancez le fichier de configuration en appelant la ligne suivante depuis le terminal :

- -
source ~/.bash_profile
- -

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 mkvirtualenv.

- -

Mise en place de l'environnement virtuel sur Windows 10

- -

Installer virtualenvwrapper-win est encore plus simple qu'installer virtualenvwrapper , 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 :

- -
pip3 install virtualenvwrapper-win
- -

Vous pouvez désormais créer un nouvel environnement virtuel avec la commande mkvirtualenv

- -

Créer un environnement virtuel

- -

Maintenant que vous avez installé virtualenvwrapper ou virtualenvwrapper-win , travailler avec des environnements virtuels sera une tâche très similaire entre chaque plateforme.

- -

Vous pouvez désormais créer un nouvel environnement virtuel avec la commande mkvirtualenv. 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).

- -
$ 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:~$
- -

Maintenant que vous êtes dans votre environnement virtuel vous pouvez installer Django et commencer à développer.

- -
-

Note : 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.

-
- -

Utiliser un environnement virtuel

- -

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) :

- - - -

Installer Django

- -

Une fois que vous avez créé votre environnement virtuel, et que vous avez utilisé workon pour y entrer, vous pouvez utiliser pip3 pour installer Django.

- -
pip3 install django
- -

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) :

- -
# Linux/macOS
-python3 -m django --version
- 2.1.5
-
-# Windows
-py -3 -m django --version
- 2.1.5
- -
-

Note : Si la commande Windows ci-dessus n'indique aucun module Django présent, essayez :

- -
py -m django --version
- -

Dans Windows, les scripts Python 3 sont exécutés en préfixant la commande avec py -3, bien que ceci puisse varier suivant votre installation. Essayer en enlevant le modificateur -3 si vous rencontrez un problème avec la commande. Dans Linux/macOS, la commande est python3.

-
- -
-

Attention : Le reste de ce module utilise les commandes Linux pour invoquer Python 3 (python3) . . Si vous travaillez sous Windows , remplacez simplement ce préfixe avec : py -3

-
- -

Tester votre installation

- -

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.

- -
mkdir django_test
-cd django_test
- -

Vous pouvez ensuite créer un nouveau site squelette intitulé "mytestsite" utilisant l'outil django-admin 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é manage.py.

- -
django-admin startproject mytestsite
-cd mytestsite
- -

Nous pouvons lancer le serveur web de développement depuis ce dossier en utilisant manage.py et la commande runserver command, comme indiqué ci-dessous.

- -
$ 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.
-
- -
-

Note : 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à !

-
- -

Maintenant que votre serveur est lancé, vous pouvez voir le site en naviguant vers l'URL suivante depuis votre navigateur local : http://127.0.0.1:8000/. Vous devriez voir un site ressemblant à celui-ci :

- -

Django Skeleton App Homepage - Django 2.0

- -

Résumé

- -

Vous avez maintenant un environnement de développement Django fonctionnel et opérationnel sur votre ordinateur.

- -

Dans la section test vous avez aussi vu comment créer un nouveau site web Django en utilisant django-admin startproject, et comment aller dessus depuis votre navigateur en utilisant le serveur de développement web (python3 manage.py runserver). Dans le prochain article nous détaillerons ce processus, et créant un application web simple mais complète.

- -

See also

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}

- -

In this module

- - diff --git a/files/fr/learn/server-side/django/development_environment/index.md b/files/fr/learn/server-side/django/development_environment/index.md new file mode 100644 index 0000000000..326bce1716 --- /dev/null +++ b/files/fr/learn/server-side/django/development_environment/index.md @@ -0,0 +1,413 @@ +--- +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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}
+ +

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.

+ + + + + + + + + + + + +
Prérequis :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.
Objectif :Avoir un environnement de développement pour Django (2.0) fonctionnel sur votre ordinateur.
+ +

Aperçu de l'environnement de développement Django

+ +

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 recommandée pour installer l'environnement de développement Django sur Ubuntu, macOS et Windows, et comment le tester.

+ +

Qu'est-ce que l'environnement de développement Django ?

+ +

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.

+ +

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 serveur web de développement 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.

+ +

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 éditeur de texte 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é.

+ +

Quelles sont les options d'installation de Django ?

+ +

Django est extrêmement flexible sur sa manière d'être installé et configuré. Django peut-être :

+ + + +

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.

+ +
+

Note : D'autres options d'installation possibles sont traitées dans la documentation officielle de Django. Les liens vers la documentation appropriée peuvent-être trouvés ci-dessous.

+
+ +

Quels systèmes d'exploitation sont supportés ?

+ +

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.

+ +

Dans cet article, nous vous donnons les instructions pour Windows, macOS et Linux/Unix.

+ +

Quelle version de Python doit-être utilisée ?

+ +

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.

+ +

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).

+ +
+

Note : 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).

+
+ +

Où peut-on télécharger Django ?

+ +

Il y a trois façons de télécharger Django :

+ + + +

Cet article explique comment installer Django depuis PyPI afin d'obtenir la version stable la plus récente.

+ +

Quelle base de données ?

+ +

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 problèmes potentiels qu'il vaut mieux éviter).

+ +

Dans cet article (et quasiment tout le module), nous utiliserons la base SQLite, 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.

+ +
+

Note : Django est configuré pour utiliser SQLite par défaut lorsque vous démarrez le projet de votre site web en utilisant les outils standards (django-admin). C'est un très bon choix lorsque vous débutez car elle ne requiert aucune configuration ou installation particulière.

+
+ +

Installation globale ou dans un environnement virtuel Python ?

+ +

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.

+ +
+

Note : 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).

+
+ +

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.

+ +

Ainsi, un développeur Python/Django confirmé lance généralement ses applications Python dans des environnements virtuels Python 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.

+ +

Ce module suppose que vous avez installé Django dans un environnement virtuel, et nous vous montrons comment le faire ci-dessous.

+ +

Installer Python 3

+ +

Si vous souhaitez utiliser Django, vous devrez installer Python sur votre système d'exploitation. Si vous utilisez Python 3, vous aurez alors aussi besoin de l'outil Python Package Indexpip3 — 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.

+ +

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.

+ +
+

Note : 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 https://www.python.org/downloads/ et les installer en utilisant la méthode appropriée à votre plateforme.

+
+ +

Ubuntu 18.04

+ +

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 :

+ +
python3 -V
+ Python 3.6.6
+ +

Toutefois, l'outil d'Index des Packages Python dont vous aurez besoin pour installer des packages avec Python 3 (y compris Django) n'est pas disponible par défaut. Vous pouvez installer pip3 avec le terminal bash avec :

+ +
sudo apt install python3-pip
+ +

macOS

+ +

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 :

+ +
python3 -V
+ -bash: python3ommand not found
+ +

Vous pouvez facilement installer Python 3 (ainsi que l'outil pip3) sur python.org:

+ +
    +
  1. Téléchargez l'installeur requis : +
      +
    1. Allez sur https://www.python.org/downloads/
    2. +
    3. Sélectionnez le bouton Download Python 3.7.2 (le numéro de version mineure peut varier).
    4. +
    +
  2. +
  3. Localisez le fichier en utilisant le Finder, puis double-cliquez le fichier package. Suivez les consignes d'installation.
  4. +
+ +

Vous pouvez désormais confirmer la bonne installation en vérifiant votre version de Python 3 comme indiqué ci-dessous :

+ +
python3 -V
+ Python 3.7.2
+
+ +

Vous pouvez aussi vérifier que pip3 est correctement installé en listant les packages disponibles :

+ +
pip3 list
+ +

Windows 10

+ +

Windows n'inclut pas Python par défaut, mais vous pouvez facilement l'installer (ainsi que l'outil pip3) sur python.org:

+ +
    +
  1. Téléchargez l'installeur requis : +
      +
    1. Allez sur https://www.python.org/downloads/
    2. +
    3. Sélectionnez le bouton Download Python 3.7.2 (le numéro de version mineure peut varier).
    4. +
    +
  2. +
  3. Installez Python en double-cliquant sur le fichier télécharger puis en suivant les consignes d'installation
  4. +
  5. Assurez-vous d'avoir coché la case intitulée "Ajouter Python au PATH".
  6. +
+ +

Vous pouvez ensuite vérifier que Python s'est correctement installé en tapant le texte suivant dans votre invite de commande :

+ +
py -3 -V
+ Python 3.7.2
+
+ +

L'installeur Windows inclut pip3 (le gestionnaire de packages Python) par défaut. Vous pouvez lister les packages installés de la manière suivante :

+ +
pip3 list
+
+ +
+

Note : 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.

+
+ +

Utiliser Django dans un environnement virtuel Python

+ +

Les librairies que nous utiliserons pour créer nos environnements virtuels seront virtualenvwrapper (Linux et macOS) et virtualenvwrapper-win (Windows), , qui à leur tour utilisent l'outil virtualenv. Les outils d'habillage permettent de créer une interface consistante pour gérer les interfaces sur toutes les plateformes.

+ +

Installer l'utilitaire d'environnement virtuel

+ +

Mise en place de l'environnement virtuel sur Ubuntu

+ +

Après avoir installé Python et pip vous pouvez installer virtualenvwrapper (qui inclut virtualenv). Le guide d'installation officiel peut être trouvé ici, ou bien vous pouvez suivre les instructions ci-dessous.

+ +

Installer l'outil en utilisant pip3:

+ +
sudo pip3 install virtualenvwrapper
+ +

Ajoutez ensuite les lignes suivantes à la fin de votre fichier de configuration shell (le fichier caché .bashrc 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 :

+ +
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
+ +
+

Note : Les variables VIRTUALENVWRAPPER_PYTHON et VIRTUALENVWRAPPER_VIRTUALENV_ARGS pointent vers l'emplacement d'installation par défaut de Python3, et source /usr/local/bin/virtualenvwrapper.sh pointe vers l'emplacement par défaut du script virtualenvwrapper.sh. Si le virtualenv 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).
+
+ Vous pourrez trouver les bons emplacements de votre système en utilisant les commandes which virtualenvwrapper.sh et which python3.

+
+ +

Puis relancez le fichier de configuration en exécutant la commande suivante dans votre terminal :

+ +
source ~/.bashrc
+ +

Vous devriez alors voir apparaître plusieurs lignes de script semblables à celles ci-dessous :

+ +
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
+ +

Vous pouvez maintenant créer un nouvel environnement virtuel avec la commande mkvirtualenv.

+ +

Mise en place de l'environnement virtuel sur macOS

+ +

L'installation de virtualenvwrapper on sur macOS est quasiment identique à celle sur Ubuntu (une fois de plus, vous pouvez suivre les instructions du guide officiel d'installation ou suivre les indications ci-dessous).

+ +

Installez virtualenvwrapper (ainsi que virtualenv) en utilisant pip comme indiqué ci-dessous.

+ +
sudo pip3 install virtualenvwrapper
+ +

Puis ajoutez les lignes suivantes à la fin de votre fichier de configuration shell.

+ +
export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh
+ +
+

Note : La variable VIRTUALENVWRAPPER_PYTHON pointe vers l'emplacement d'installation par défaut de Python3, et source /usr/local/bin/virtualenvwrapper.sh pointe vers l'emplacement par défaut du script virtualenvwrapper.sh. Si le virtualenv 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).

+ +

Par exemple, une installation test sur macOS a résulté en l'ajout des lignes suivantes dans le fichier startup :

+ +
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
+ +

Vous pourrez trouver les bons emplacements de votre système en utilisant les commandes which virtualenvwrapper.sh et which python3.

+
+ +

Ce sont les mêmes lignes que pour Ubuntu, mais le nom du fichier de configuration caché du répertoire home est différent : .bash_profile dans votre répertoire home.

+ +
+

Note : Si vous n'arrivez pas à trouver le fichier .bash_profile depuis le finder, vous pouvez aussi l'ouvrir depuis le terminal en utilisant nano.

+ +

La commande sera la suivante :

+ +
>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.
+
+ +

Puis relancez le fichier de configuration en appelant la ligne suivante depuis le terminal :

+ +
source ~/.bash_profile
+ +

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 mkvirtualenv.

+ +

Mise en place de l'environnement virtuel sur Windows 10

+ +

Installer virtualenvwrapper-win est encore plus simple qu'installer virtualenvwrapper , 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 :

+ +
pip3 install virtualenvwrapper-win
+ +

Vous pouvez désormais créer un nouvel environnement virtuel avec la commande mkvirtualenv

+ +

Créer un environnement virtuel

+ +

Maintenant que vous avez installé virtualenvwrapper ou virtualenvwrapper-win , travailler avec des environnements virtuels sera une tâche très similaire entre chaque plateforme.

+ +

Vous pouvez désormais créer un nouvel environnement virtuel avec la commande mkvirtualenv. 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).

+ +
$ 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:~$
+ +

Maintenant que vous êtes dans votre environnement virtuel vous pouvez installer Django et commencer à développer.

+ +
+

Note : 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.

+
+ +

Utiliser un environnement virtuel

+ +

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) :

+ + + +

Installer Django

+ +

Une fois que vous avez créé votre environnement virtuel, et que vous avez utilisé workon pour y entrer, vous pouvez utiliser pip3 pour installer Django.

+ +
pip3 install django
+ +

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) :

+ +
# Linux/macOS
+python3 -m django --version
+ 2.1.5
+
+# Windows
+py -3 -m django --version
+ 2.1.5
+ +
+

Note : Si la commande Windows ci-dessus n'indique aucun module Django présent, essayez :

+ +
py -m django --version
+ +

Dans Windows, les scripts Python 3 sont exécutés en préfixant la commande avec py -3, bien que ceci puisse varier suivant votre installation. Essayer en enlevant le modificateur -3 si vous rencontrez un problème avec la commande. Dans Linux/macOS, la commande est python3.

+
+ +
+

Attention : Le reste de ce module utilise les commandes Linux pour invoquer Python 3 (python3) . . Si vous travaillez sous Windows , remplacez simplement ce préfixe avec : py -3

+
+ +

Tester votre installation

+ +

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.

+ +
mkdir django_test
+cd django_test
+ +

Vous pouvez ensuite créer un nouveau site squelette intitulé "mytestsite" utilisant l'outil django-admin 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é manage.py.

+ +
django-admin startproject mytestsite
+cd mytestsite
+ +

Nous pouvons lancer le serveur web de développement depuis ce dossier en utilisant manage.py et la commande runserver command, comme indiqué ci-dessous.

+ +
$ 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.
+
+ +
+

Note : 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à !

+
+ +

Maintenant que votre serveur est lancé, vous pouvez voir le site en naviguant vers l'URL suivante depuis votre navigateur local : http://127.0.0.1:8000/. Vous devriez voir un site ressemblant à celui-ci :

+ +

Django Skeleton App Homepage - Django 2.0

+ +

Résumé

+ +

Vous avez maintenant un environnement de développement Django fonctionnel et opérationnel sur votre ordinateur.

+ +

Dans la section test vous avez aussi vu comment créer un nouveau site web Django en utilisant django-admin startproject, et comment aller dessus depuis votre navigateur en utilisant le serveur de développement web (python3 manage.py runserver). Dans le prochain article nous détaillerons ce processus, et créant un application web simple mais complète.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}

+ +

In this module

+ + diff --git a/files/fr/learn/server-side/django/forms/index.html b/files/fr/learn/server-side/django/forms/index.html deleted file mode 100644 index 2d63644055..0000000000 --- a/files/fr/learn/server-side/django/forms/index.html +++ /dev/null @@ -1,651 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}
- -

Dans cette formation nous allons vous montrer comment travailler avec les formulaires HTML sous Django afin de créer, modifier et supprimer des instances de modèle. Pour illustrer le raisonnement, nous allons étendre le site web LocalLibrary 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. 

- - - - - - - - - - - - -
Prérequis:Avoir terminé les formations précédentes, y compris Django Tutorial Part 8: User authentication and permissions.
Objectifs: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.   
- -

Vue d'ensemble

- -

Un formulaire HTML regroupe au moins un champ remplissable et des composants élémentaires d'interface web.  Il peut être utilisé pour réunir des saisies de la part des utilisateurs avant envoi vers un serveur. Les formulaires sont souples: ils s'adaptent à plusieurs modes de saisie. En effet, Il existe des composants élementaires d'interfaces graphique pour des  modes de saisie non contrainte avec une zone de saisie de texte, ou resteinte au type date avec un date picker,  la saisie d'un variable optionnelle via une boîte à cocher,  d'un choix à faire parmi plusieurs valeurs possibles avec les boutons radio etc... . Les formulaires permettent de partager des informations avec le serveur de manière relativement sécurisée  car ils permettent d'envoyer des requêtes de  type POST protégeant de la falsification des requêtes inter-site.

- -

Bien que nous n'ayons pas encore créé de formulaire au cours de cette formation, nous en avons déjà rencontré sur l'interface d'administration Django Admin — par exemple la capture d'écran ci-dessous montre un formulaire d'édition  de l'un de nos modèle de Book  (livre), comprenant des composants élémentaires d'interface graphique de chois de valeur parmi une liste proposée,  et des zones des saisie de texte.

- -

Admin Site - Book Add

- -

Travailler avec des formulaires peut s'avérer compliqué ! Les développeurs doivent non seulement écrire le code  HTML pour le formulaire, mais aussi vérifier et corriger sur le serveur les données saisies (et éventuellement aussi dans le navigateur), renvoyer le formulaire avec des messages d'erreur pour informer les usagers de tout champ invalide, prendre en charge les données quand elles passent l'étape de vérification, et finalement renvoyer une information à l'utilisateur d'une manière ou d'une autre pour indiquer ce succès. Les formulaires sous Django enlèvent beaucoup de travail à chacune de ces étapes, grâce à un cadriciel qui permet de déclarer des formulaires et leurs champs à travers un langage de programmation, puis d'utiliser ces objets non seulement pour générer le code HTML, mais aussi une grosse partie de la vérification des données et du retour d'information à l'utilisateur.

- -

Dans cette formation, nous allons vous montrer quelque-unes des manièrs de créer et de travailler avec les formulaires, et en particulier, comment les vues sur les formulaires génériques d'édition peuvent réduire significativement la quantité de travail à fournir pour créer les formulaires de manipulation de vos modèles. En chemin nous allons étendre notre application LocalLibrary en ajoutant un formulaire permettant aux bibliothécaires de prolonger le prêt de libres, et nous allons créer des pages pour créer, modifier et supprimer des livres et des auteurs (reproduisant une version basique du formulaire ci-dessus pour éditer des livres. )

- -

Formulaires HTML

- -

D'abord, un premier aperçu des formulaires HTML (HTML Forms). Soit un formulaire HTML simple, composé d'un unique champ de saisie texte , présent pour y entrer le nom d'une "équipe" quelconque, et son sa description dans l'étiquette associée :

- -

Simple name field example in HTML form

- -

Le formulaire est défini en HTML comme une collection d'éléments enfermés entre deux balises <form> ... </form> contenant au moins une balise <input> dont la valeur d'attribut 'type' doit valoir "submit":

- -
<form action="/team_name_url/" method="post">
-    <label for="team_name">Enter name: </label>
-    <input id="team_name" type="text" name="name_field" value="Default name for team.">
-    <input type="submit" value="OK">
-</form>
-
- -

Bien qu'ici nous n'ayons qu'un champ de saisie texte destiné à recevoir le nom d'équipe, une formulaire pourrait avoir un nombre quelconque d'autres champs de saisie et leurs étiquettes de description associées. La valeur de l'attribut 'type' définit la sorte de composant élementaire d'interface graphique  affichée. Les attributs 'id' et 'name' permettent d'identifier le champ en JavaScript/CSS/HTML alors que l'attribut 'value' définit la valeur initiale du champ lorsqu'il est affiché pour la première fois. La description associée  est déclarée par la balise <label> (voir "Enter Name" au dessus) , avec un attribut 'for' devant contenir la valeur de l'attribut 'id' du champ imput à laquelle on souhaite l'associer.  

- -

La balise <input> dont l'attribut 'type' vaut submit sera affichée (par défaut) comme un bouton  qui peut être cliqué par l'utilisateur pour envoyer vers le serveur les données figurant dans tous les autres éléments de formulaire <input> (dans le cas présent, la valeur actuelle de 'team name'. Les attributs de formulaire déterminent d'une part la méthode HTTP (attribut method) utilisée pour envoyer les donnnées et d'autre part la destination des données sur le serveur (attribut action ):

- - - -

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 .  

- -

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!

- -

Les étapes de gestion d'un formulaire avec Django

- -

Django gère un formulaire en utilisant les mêmes techniques qu'évoquées lors des formations précédentes (pour afficher des informations à propos de nos modèles): La vue reçoit une requête, exécute toute acion nécessaire, incluant la lecture de données depuis les modèles, puis génère une page HTML (à partir d'un squelette à qui nous transmettons un contexte contenant les données à afficher ). Ce qui rend les choses plus compliquées, c'est que le serveur a aussi besoin d'être capable de traiter les données fournies par l'utilisateur (pas seulement le contexte) et doit pouvoir réafficher les pages s'il y a une quelconque erreur.

- -

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).

- -

Updated form handling process doc.

- -

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 : 

- -
    -
  1. Afficher le formulaire sous sa forme par défaut la première fois où il est demandé par l'utilisateur. -
      -
    • Le formulaire peut contenir des champs vides (par exemple si vous créez un nouvel enregistrement ) ou peut être prérempli de valeurs initiales (par exemple si vous modifiez les valeurs d'un enregistrement existant, ou que ces champs ont des valeurs initiales utiles ).
    • -
    • Le formulaire est qualifié à cette étape de formulaire libre, parce qu'il n'est associé à aucune donnée entré par l'utilisateur (bien qu'il puisse avoir des valeurs initiales) . 
    • -
    -
  2. -
  3. Recevoir des données d'une requete d'envoi de données et les lier au formulaire. -
      -
    • 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. 
    • -
    -
  4. -
  5. Nettoyer et valider les données -
      -
    • Le nettoyage de données consiste à désinfecter la saisie (par exemple en supprimant les caractères non valides, et qui pourraient être utilisés pour envoyer du contenu malveillant au serveur.) et à convertir ces données en types Python cohérents. 
    • -
    • La validation vérifie que les valeurs envoyées sont appropriées au champ (par exemple dans le bon intervalle de dates, ni trop long ni trop court, etc.) 
    • -
    -
  6. -
  7. 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.
  8. -
  9. 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.)
  10. -
  11. Une fois toutes ces actions accomplies, redirige l'utilisateur vers une autre page.
  12. -
- -

Django fournit une multitude d'outils et de méthodes pour vous assister dans les tâches mentionnées ci-dessus. Parmi eux la plus importante, la classe Form, qui simplifie à la fois la production de formulaire HTML mais aussi la validation de donnée. In the next section we describe how forms work using the practical example of a page to allow librarians to renew books.

- -
-

Note : Comprendre l'utilisation de Form vous aidera quand nous parlerons des classes de formulaires de Django plus complexes. 

-
- -

Formulaire de renouvellement de livre par l'utilisation de vue Form

- -

Nous allons maintenant créer une page qui permettra aux bibliothécaires de renouveler les livres empruntés (les rendre disponible à nouveau). Pour cela nous allons créer un formulaire qui permet aux utilisateurs de saisir une valeur de type Date. Considérons le champ avec une valeur initiale égale à la date du jour plus 3 semaines (la période normale d'emprunt d'un livre), et ajouter une validation pour s'assurer que le bibliothécaire ne peut pas saisir une date dans le passé ou une date trop éloignée dans le futur. Quand une date valide a été entrée, nous l'enregistrons dans le champ BookInstance.due_back de l'enregistrement courant.

- -

L'exemple va utiliser une vue basée sur une fonction et une classe Form. Les sections suivantes expliquent comment les formulaires fonctionnent, et les changements que vous devez faire à notre projet en cours LocalLibrary.

- -

Formulaire

- -

La classe Form est le cœur du système de gestion des formulaires de Django. Elle spécifie les champs présents dans le formulaire, affiche les widgets, les labels, les valeurs initiales, les valeurs valides et (après validation) les messages d'erreur associés aux champs invalides. Cette classe fournit également des méthodes pour se restituer elle-même dans les templates en utilisant des formats prédéfinis (tables, listes etc.) ou pour obtenir la valeur de chaque élément de formulaire (permettant un rendu manuel fin).

- -

Déclarer un formulaire

- -

La syntaxe de déclaration pour un Form est très semblable à celle utilisée pour déclarer un Model, et partage les mêmes types de champs (et des paramètres similaires). Cela est logique, puisque dans les deux cas nous avons besoin de nous assurer que chaque champ gère le bon type de donnée, est contraint lors de la validation des données, et a une description pour l'affichage/la documentation.

- -

Les données de formulaire sont stockées dans un fichier application forms.py, à l'intérieur du répertoire de l'application. Créez et ouvrez le fichier locallibrary/catalog/forms.py. Pour créer un Form, nous importons la bibliothèque forms, dérivons une classe de la classe Form, et déclarons les champs du formulaire. Une classe très basique de formulaire pour notre formulaire de renouvellement de livre dans notre bibliothèque est montrée ci-dessous :

- -
from django import forms
-
-class RenewBookForm(forms.Form):
-    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
-
- -

Champs de formulaire

- -

Dans ce cas, nous avons un unique champ DateField pour entrer la date du renouvellement, qui sera rendue en HTML avec une valeur vide, le label par défaut "Renewal date:", et un texte utilitaire indiquant comment s'en servir : "Enter a date between now and 4 weeks (default 3 weeks)." Comme aucun des autres arguments optionnels ne sont spécifiés, le champ acceptera des dates en utilisant les input_formats suivants : YYYY-MM-DD (2016-11-06), MM/DD/YYYY (02/26/2016), MM/DD/YY (10/25/16), et sera rendu en utilisant le widget par défaut : DateInput.

- -

Il y a beaucoup d'autres types de champs, que vous reconnaîtrez sans peine en raison de leur ressemblance avec les classes de champs équivalentes pour les modèles : BooleanField, CharField, ChoiceField, TypedChoiceField, DateField, DateTimeField, DecimalField, DurationField, EmailField, FileField, FilePathField, FloatField, ImageField, IntegerField, GenericIPAddressField, MultipleChoiceField, TypedMultipleChoiceField, NullBooleanField, RegexField, SlugField, TimeField, URLField, UUIDField, ComboField, MultiValueField, SplitDateTimeField, ModelMultipleChoiceField, ModelChoiceField​​​​.

- -

Les arguments communs de la plupart des champs sont listés ci-dessous (ils ont les valeurs les plus communes par défaut) :

- - - -

Validation

- -

Django fournit un grand nombre d'endroits pour valider vos données. La façon la plus simple de valider un champ unique est de remplacer la méthode clean_<fieldname>() pour le champ à vérifier. Ainsi, par exemple, nous pouvons vérifier que les valeurs entrées pour le champ renewal_date sont entre maintenant et dans 4 semaines, en implémentant la méthode clean_renewal_date() comme montré ci-après.

- -

Mettez à jour votre fichier forms.py, de telle sorte qu'il ressemble à cela :

- -
from django import forms
-
-from django.core.exceptions import ValidationError
-from django.utils.translation import ugettext_lazy as _
-import datetime #for checking renewal date range.
-
-class RenewBookForm(forms.Form):
-    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 date is not in past.
-        if data < datetime.date.today():
-            raise ValidationError(_('Invalid date - renewal in past'))
-
-        #Check date is in range librarian allowed to change (+4 weeks).
-        if data > 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
- -

Il y a deux choses importantes à noter. La première est que nous accédons à nos données en utilisant self.cleaned_data['renewal_date'], et que nous retournons ces données, que nous les ayons changées ou non, à la fin de la fonction. Cette étape nous donne des données "nettoyées", purgées de valeurs potentiellement dangereuses en utilisant les validateurs par défaut, et converties en type standard correct pour les données considérées (dans ce cas un objet Python datetime.datetime).

- -

Le deuxième point est que, si une valeur tombe en dehors de l'intervalle que nous avons autorisé, nous levons une ValidationError, en spécifiant le texte d'erreur que nous voulons afficher dans la zone du formulaire prévue pour le cas où l'utilisateur entre une valeur incorrecte. L'exemple ci-dessus enveloppe aussi ce texte dans ugettext_lazy() (importée comme _()), une des fonctions de traduction Django, ce qui est une bonne pratique si vous voulez traduire votre site plus tard.

- -
-

Note : Il y a un grand nombre d'autres méthodes et exemples au sujet de la validation des formulaires dans Form and field validation (Django Docs). Par exemple, au cas où vous avez plusieurs champs dépendants les uns des autres, vous pouvez réécrire la fonction Form.clean(), et lever de nouveau une ValidationError.

-
- -

C'est tout ce dont nous avons besoin pour notre formulaire dans cet exemple.

- -

Configuration d'URL

- -

Avant de créer notre vue, ajoutons une configuration d'URL pour la page renew-books. Copiez la configuration suivante à la fin de locallibrary/catalog/urls.py.

- -
urlpatterns += [
- url(r'^book/(?P<pk>[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'),
-]
- -

La configuration d'URL va rediriger les URLs ayant le format /catalog/book/<bookinstance id>/renew/ vers la fonction appelée renew_book_librarian() dans views.py, et envoyer l'id de BookInstance comme paramètre sous le nom pk. Le pattern ne fonctionnera que si pk est un uuid correctement formaté.

- -
-

Note : Nous pouvons appeler comme bon nous semble la donnée d'URL "pk" que nous avons capturée, car nous contrôlons complètement la fonction de notre view (nous n'utilisons pas une vue générique detail, laquelle attendrait des paramètres avec un certain nom). Cependant, le raccourci pk, pour "primary key", est une convention qu'il est raisonnable d'utiliser !

-
- -

Vue

- -

Comme nous l'avons expliqué ci-dessus dans Django form handling process, la vue doit retourner le formulaire par défaut s'il est appelé pour la première fois, et ensuite soit le retourner à nouveau avec les messages d'erreur si les données sont invalides, soit gérer les données et rediriger vers une nouvelle page si elles sont valides. Pour effectuer ces différentes actions, la vue doit être en mesure de savoir si elle est appelée pour la première fois (et retourner le formulaire par défaut) ou pour la deuxième fois ou plus (et valider les données).

- -

Pour les formulaires qui utilisent une requête POST pour envoyer une information au serveur, la manière la plus commune de procéder pour la vue est de tester le type de requête POST (if request.method == 'POST':) pour repérer des requêtes de type validation de formulaire, et GET (en utilisant une condition else) pour identifer une requête initiale de création de formulaire. Si vous voulez utiliser une requête GET pour envoyer vos données, alors une approche classique pour savoir si la vue est invoquée pour la première fois ou non est de lire les données du formulaire (p. ex. lire une valeur cachée dans le formulaire).

- -

Le processus de renouvellement de livre va écrire dans notre base de données, aussi, par convention, nous utiliserons le type de requête POST. Le bout de code ci-dessous montre le procédé (très classique) pour cette sorte de vue basée sur des fonctions.

- -
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
-    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})
- -

Nous importons tout d'abord notre formulaire (RenewBookForm) et un certain nombre d'autres objets/méthodes utiles, dont nous nous servons dans le corps de la fonction de notre vue :

- - - -

Dans la vue, nous utilisons d'abord l'argument pk dans la fonction get_object_or_404(), afin d'obtenir la BookInstance courante (si cette instance n'existe pas, la vue se termine immédiatement et la page va afficher une erreur ). Si ce n'est pas une requête POST (cas géré par la clause else), alors nous créons le formulaire par défaut en lui passant une valeur initial pour le champ renewal_date (comme montré en gras ci-dessous, c'est la date actuelle plus 3 semaines).

- -
 book_inst=get_object_or_404(BookInstance, pk = pk)
-
-    # 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})
- -

Après création du formulaire, nous appelons la fonction render() pour créer la page HTML, en précisant le template et un contexte qui contient notre formulaire. Dans ce cas, le contexte contient aussi notre BookInstance, que nous allons utiliser dans le template pour fournir des informations à propos du livre que nous sommes en train de renouveler.

- -

En revanche, s'il s'agit d'une requête POST, alors nous créons notre objet form et le peuplons avec des données récupérées dans la requête. Ce processus est appelé "binding" (liaison) et nous permet de valider le formulaire. Ensuite nous vérifions que le formulaire est valide, ce qui déclenche tout le code de validation sur tous les champs - ce qui inclut à la fois le code générique vérifiant que notre champ date est effectivement une date valide, et notre fonction clean_renewal_date(), spécifique à notre formulaire, pour vérifier que la date est dans le bon intervalle.

- -
    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') )
-
-    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
- -

Si le formulaire n'est pas valide, nous appelons aussi la fonction render(), mais cette fois les valeurs passées dans le contexte vont inclure les messages d'erreur.

- -

Si le formulaire est valide, alors nous pouvons commencer à utiliser les données, en y accédant à travers l'attribut form.cleaned_data (p. ex. data = form.cleaned_data['renewal_date']). Ici nous ne faisons que sauvegarder dans la valeur due_back de l'objet BookInstance associé les données reçues.

- -
-

Attention : Alors que vous pouvez accéder aussi aux données de formulaire directement à travers la requête (par exemple request.POST['renewal_date'], ou request.GET['renewal_date'] si vous utilisez une requête GET), ce n'est PAS recommandé. Les données nettoyées sont assainies, validées et converties en types standard Python.

-
- -

L'étape finale dans la partie "gestion de formulaire" de la vue est de rediriger vers une autre page, habituellement une page "success". Dans ce cas, nous utilisons HttpResponseRedirect et reverse() pour rediriger vers la vue appelée 'all-borrowed' (qui a été créée dans la partie "challenge" de Django Tutorial Part 8: User authentication and permissions. Si vous n'avez pas créé cette page, vous pouvez rediriger vers la page d'accueil à l'URL '/').

- -

C'est tout ce qui est requis pour la gestion du formulaire lui-même, mais il nous faut encore restreindre l'accès à la vue aux seuls libraires. Nous devrions sans doute créer un nouveau réglage de permission dans BookInstance ("can_renew"), mais, pour ne pour ne pas compliquer les choses ici, nous allons simplement utiliser le décorateur de fonction @permission_required avec notre permission existante can_mark_returned.

- -

Le résultat final de la vue est donc comme indiqué ci-dessous. Veuillez copier ceci en bas de locallibrary/catalog/views.py.

- -
from django.contrib.auth.decorators import permission_required
-
-from django.shortcuts import get_object_or_404
-from django.http import HttpResponseRedirect
-from django.core.urlresolvers import reverse
-import datetime
-
-from .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_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})
-
- -

Le template

- -

Créez le template référencé dans la vue (/catalog/templates/catalog/book_renew_librarian.html) et copiez-y le code suivant :

- -
{% extends "base_generic.html" %}
-{% block content %}
-
- <h1>Renew: \{{bookinst.book.title}}</h1>
- <p>Borrower: \{{bookinst.borrower}}</p>
- <p{% if bookinst.is_overdue %} class="text-danger"{% endif %}>Due date: \{{bookinst.due_back}}</p>
-
- <form action="" method="post">
- {% csrf_token %}
- <table>
- \{{ form }}
-  </table>
- <input type="submit" value="Submit" />
- </form>
-
-{% endblock %}
- -

La majeure partie de ce code devrait vous être familière si vous avez suivi les tutoriels précédents. Nous étendons le template de base et ensuite redéfinissons le block "content". Nous sommes en mesure de référencer \{{ book_instance }} (et ses variables), puisqu'il a été passé dans l'objet context par la fonction render(), et nous utilisons tout cela pour lister le titre du livre, son emprunteur et la date originale de retour.

- -

Le code du formulaire est relativement simple. Nous déclarons d'abord les tags form, en précisant où le formulaire doit être adressé (action) et la method utilisée pour soumettre les donées (ici un "HTTP POST"). Si vous vous rappelez ce qui a été dit en haut de cette page (aperçu sur les HTML Forms), une action vide comme ici signifie que les données de formulaire seront postées à nouveau à l'URL actuelle (ce qui est le comportement que nous voulons !). À l'intérieur des tags, nous définissons le bouton , sur lequel l'utilisateur peut appuyer pour envoyer les données. Le {% csrf_token %} ajouté juste à l'intérieur des tags "form" est un des éléments de protection utilisés par Django contre les "cross-site forgery".

- -
-

Note : Ajoutez le {% csrf_token %} à tout template Django que vous créeez et qui utilise POST pour soumettre les données. Cela réduira les risques qu'un utilisateur mal intentionné pirate vos formulaires.

-
- -

Tout ce qui reste est la variable de template \{{ form }}, que nous avons passée au template dans le dictionnaire de contexte. Peut-être sans surprise, quand il est utilisé comme indiqué, il fournit le rendu par défaut de tous les champs de formulaire, y compris leurs labels, widgets et textes d'aide. Voici le rendu :

- -
<tr>
-  <th><label for="id_renewal_date">Renewal date:</label></th>
-  <td>
-  <input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required />
-  <br />
-  <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
-  </td>
-</tr>
-
- -
-

Note : Ce n'est peut-être pas évident, car nous n'avons qu'un seul champ, mais, par défaut, chaque champ est défini dans sa propre ligne de tableau. Ce même rendu est fourni si vous référencez la variable de template \{{ form.as_table }}.

-
- -

Si vous aviez entré une date invalide, vous obtiendriez en plus sur la page une liste des erreurs (indiquées en gras ci-dessous).

- -
<tr>
-  <th><label for="id_renewal_date">Renewal date:</label></th>
-  <td>
-  <ul class="errorlist">
-  <li>Invalid date - renewal in past</li>
-  </ul>
-  <input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required />
-  <br />
-  <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
- </td>
-</tr>
- -

Autres façons d'utiliser la variable de template du formulaire

- -

Si vous utilisez \{{ form.as_table }} comme indiqué ci-dessus, chaque champ est rendu comme une ligne de tableau. Vous pouvez également rendre chaque champ comme un élément de liste (en utilisant \{{ form.as_ul }}) ou comme un paragraphe (en utilisant \{{ form.as_p }}).

- -

Il est également possible d'avoir un contrôle complet sur le rendu de chaque partie du formulaire, en indexant ses propriétés grâce à la notation pointée. Ainsi, par exemple, nous pouvons accéder un certain nombre d'éléments distincts pour notre champ renewal_date :

- - - -

Pour plus d'exemples sur la manière de rendre manuellement des formulaires dans des templates, et boucler de manière dynamique sur les champs du template, voyez Working with forms > Rendering fields manually (Django docs).

- -

Tester la page

- -

Si vous avez accepté le "challenge" dans Django Tutorial Part 8: User authentication and permissions, 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.

- -
{% if perms.catalog.can_mark_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a> {% endif %}
- -
-

Note : Souvenez-vous que votre login de test devra avoir la permission "catalog.can_mark_returned" pour pouvoir accéder la page de renouvellement de livre (utilisez peut-être votre compte superuser).

-
- -

Vous pouvez aussi construire manuellement une URL de test comme ceci : http://127.0.0.1:8000/catalog/book/<bookinstance_id>/renew/ (un id de bookinstance valide peut être obtenu en navigant vers une page de détail de livre dans votre bibliothèque, et en copiant le champ id).

- -

À quoi cela ressemble-t-il ?

- -

Si tout a bien marché, le formulaire par défaut ressemblera à ceci :

- -

- -

Le formulaire avec valeur erronée ressemblera à ceci :

- -

- -

La liste de tous les livres avec les liens vers le renouvellement ressemblera à ceci :

- -

- -

ModelForms

- -

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).

- -

Cependant, si vous avez seulement besoin d'un formulaire qui répertorie les champs d'un modèle unique, alors votre modèle définira déjà la plupart des informations requises dans votre formulaire : champs, labels, texte d'aide etc. Plutôt que de créer à nouveau les définitions du modèle dans votre formulaire, il est plus facile d'utiliser la classe d'aide ModelForm pour créer le formulaire d'après votre modèle. Ce ModelForm peut dès lors être utilisé à l'intérieur de vos vues exactement de la même manière qu'un Form ordinaire.

- -

Un ModelForm basique, contenant le même champ que notre RenewBookForm d'origine, est montré ci-dessous. Tout ce que vous avez à faire pour créer le formulaire, c'est ajouter class Meta avec le model (BookInstance) associé, et une liste des fields du modèle à inclure dans le formulaire (vous pouvez inclure tous les champs en utilisant fields = '__all__', ou bien utiliser exclude (au lieu de fields) pour préciser les champs à ne pas importer du modèle).

- -
from django.forms import ModelForm
-from .models import BookInstance
-
-class RenewBookModelForm(ModelForm):
- class Meta:
- model = BookInstance
- fields = ['due_back',]
-
- -
-

Note : Cela peut ne pas sembler beaucoup plus simple que d'utiliser un simple Form, et ça ne l'est effectivement pas dans ce cas, parce que nous n'avons qu'un seul champ. Cependant, si vous avez beaucoup de champs, cela peut réduire notablement la quantité de code !

-
- -

Le reste de l'information vient des définitions de champ données par le modèle (par ex. les labels, les widgets, le texte d'aide, les messages d'erreur). S'ils ne sont pas suffisamment satisfaisants, nous pouvons les réécrire dans notre class Meta, en précisant un dictionnaire contenant le champ à modifier et sa nouvelle valeur. Par exemple, dans ce formulaire, nous pourrions souhaiter, pour notre champ, un label tel que "Renewal date" (plutôt que celui par défaut, basé sur le nom du champ : Due Back), et nous voulons aussi que notre texte d'aide soit spécifique à ce cas d'utilisation. La classe Meta ci-dessous vous montre comment réécrire ces champs, et vous pouvez pareillement définir widgets et error_messages si les valeurs par défaut ne sont pas suffisantes.

- -
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).'), } 
-
- -

Pour ajouter une validation, vous pouvez utiliser la même approche que pour un Form normal : vous définissez une fonction appelée clean_field_name(), et vous levez des exceptions de type ValidationError pour les valeurs non valides. La seule différence par rapport à notre formulaire original, c'est que le champ de modèle est appelé due_back et non "renewal_date". Ce changement est nécessaire, dans la mesure où le champ correspondant dans BookInstance est appelé due_back.

- -
from django.forms import ModelForm
-from .models import BookInstance
-
-class RenewBookModelForm(ModelForm):
-    def clean_due_back(self):
-       data = self.cleaned_data['due_back']
-
-  #Check date is not in past.
-       if data < datetime.date.today():
-           raise ValidationError(_('Invalid date - renewal in past'))
-
-       #Check date is in range librarian allowed to change (+4 weeks)
-       if data > 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
-
- 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).'), }
-
- -

La classe RenewBookModelForm ci-dessus est maintenant fonctionnellement équivalente à notre RenewBookForm d'origine. Vous pourriez l'importer et l'utiliser partout où vous utilisez RenewBookForm, du moment que vous changez aussi de renewal_date en due_back le nom de variable du formulaire correspondant, comme dans la deuxième déclaration du formulaire : RenewBookModelForm(initial={'due_back': proposed_renewal_date}.

- -

Vues génériques d'édition

- -

L'algorithme de gestion des formulaires que nous avons utilisé ci-dessus dans notre exemple de vue basée sur une fonction, représente un processus extrêmement commun dans vues destinées à éditer un formulaire. Django abstrait pour vous la plus grande partie de ce processus répétitif ("boilerplate") en proposant des generic editing views pour les vues de création, éditition et suppression basées sur des modèles. Ces vues génériques non seulement assument le comportement d'une vue, mais elles créent automatiquement la classe de formulaire (un ModelForm) pour vous à partir du modèle.

- -
-

Note : En plus des vues d'édition décrites ici, il existe aussi une classe FormView, qui se tient, en termes de rapport "flexibilité"/"effort codage", à mi-chemin entre notre vue basée sur une fonction et les autres vues génériques. En utilisant FormView, vous avez encore besoin de créer votre Form, mais vous n'avez pas besoin d'implémenter tous les éléments d'une gestion standard de formulaire. À la place, vous n'avez qu'à fournir une implémentation de la fonction qui sera appelée une fois que les données envoyées sont reconnues valides.

-
- -

Dans cette section, nous allons utiliser des vues génériques d'édition pour créer des pages afin de pouvoir ajouter les fonctionnalités de création, d'édition et de suppression des enregistrements de type Author de notre bibliothèque, en fournissant efficacement une réimplémentation basique de certaines parties du site Admin (cela peut être intéressant si vous avez besoin d'offrir une fonctionnalité admin d'une manière plus flexible que ce qui peut être présenté par le site admin).

- -

Vues

- -

Ouvrez le fichier vue (locallibrary/catalog/views.py) et ajoutez le bloc de code suivant à la fin :

- -
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')
- -

Comme vous pouvez le voir, pour les vues "créer", "modifier" et "supprimer", vous avez besoin de dériver respectivement des vues génériques CreateView, UpdateView, et DeleteView, et de définir ensuite le modèle associé.

- -

Pour les cas "créer" et "modifier", vous devez aussi préciser les champs à afficher dans le formulaire (en utilisant la même syntaxe que pour la classe ModelForm). Dans ce cas, nous montrons à la fois la syntaxe pour afficher "tous" les champs, et comment vous pouvez les lister un par un. Vous pouvez aussi spécifier les valeurs initiales pour chacun des champs, en utilisant un dictionnaire de paires nom_du_champ/valeur (ici nous définissons arbitrairement la date de mort, uniquement dans un but de démonstration - sans doute voudrez-vous l'enlever !). Par défaut, ces vues vont rediriger en cas de succès vers une page affichant l'élément nouvellement créé ou modifié, ce qui, dans notre cas, sera la vue "détail" d'un auteur, créée dans un précédent tutoriel. Vous pouvez spécifier un autre lieu de redirection en déclarant explicitement le paramètre success_url (comme indiqué dans la classe AuthorDelete).

- -

La classe  ne requiert pas l'affichage d'aucun champ, aussi n'ont-ils pas besoin d'être précisés. Par contre il vous faut bien spécifier la success_url, car Django n'a pas de valeur par défaut pour cela. Dans ce cas, nous utilisons la fonction  pour rediriger vers notre liste d'auteurs après qu'un auteur ait été supprimé. reverse_lazy() est une version de reverse() exécutée mollement ("lazily"), que nous utilisons ici parce que nous fournissons une URL à un attribut de vue basée sur classe.

- -

Templates

- -

Les vues "créer" et "modifier" utilisent le même template par défaut, lequel sera nommé d'après votre modèle : model_name_form.html (vous pouvez changer le suffixe en autre chose que _form en utilisant le champ template_name_suffix dans votre vue, par exemple template_name_suffix = '_other_suffix').

- -

Créez le fichier de template locallibrary/catalog/templates/catalog/author_form.html, et copiez-y le texte suivant.

- -
{% extends "base_generic.html" %}
-
-{% block content %}
-
-<form action="" method="post">
- {% csrf_token %}
- <table>
- \{{ form.as_table }}
- </table>
- <input type="submit" value="Submit" />
-
-</form>
-{% endblock %}
- -

Ce formulaire est semblable à nos formulaires précédents et affiche les champs en utilisant un tableau. Notez aussi comment nous déclarons à nouveau le {% csrf_token %} pour nous assurer que nos formulaires résisteront à d'éventuelles attaques par CSRF (Cross Site Request Forgery).

- -

La vue "supprimer" s'attend à trouver un template avec un nom au format model_name_confirm_delete.html (de nouveau, vous pouvez changer le suffixe en utilisant template_name_suffix dans votre vue). Créez le fichier de template locallibrary/catalog/templates/catalog/author_confirm_delete.html, et copiez-y le texte suivant.

- -
{% extends "base_generic.html" %}
-
-{% block content %}
-
-<h1>Delete Author</h1>
-
-<p>Are you sure you want to delete the author: \{{ author }}?</p>
-
-<form action="" method="POST">
- {% csrf_token %}
- <input type="submit" action="" value="Yes, delete." />
-</form>
-
-{% endblock %}
-
- -

Configurations d'URL

- -

Ouvrez votre fichier de configuration d'URL (locallibrary/catalog/urls.py) et ajoutez-y à la fin la configuration suivante :

- -
urlpatterns += [
- url(r'^author/create/$', views.AuthorCreate.as_view(), name='author_create'),
- url(r'^author/(?P<pk>\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'),
- url(r'^author/(?P<pk>\d+)/delete/$', views.AuthorDelete.as_view(), name='author_delete'),
-]
- -

Il n'y a rien de particulièrement nouveau ici ! Vous pouvez voir que les vues sont des classes, et doivent dès lors être appelée via .as_view(), et vous devriez être capable de reconnaître les patterns d'URL dans chaque cas. Nous devons utiliser pk comme nom pour la valeur de nos clés primaires capturées, car c'est le nom de paramètre attendu par les classes de vue.

- -

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).

- -
-

Note : Les utilisateurs observateurs auront remarqué que nous n'avons rien fait pour empêcher les utilisateurs non autorisés d'accéder ces pages ! Nous laissons cela comme exercice pour vous (suggestion : vous pourriez utiliser le PermissionRequiredMixin, et soit créer une nouvelle permission, soit réutiliser notre permissioncan_mark_returned ).

-
- -

Test de la page

- -

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.

- -

Ensuite naviguez à la page de création d'auteur : http://127.0.0.1:8000/catalog/author/create/, ce qui devrait ressembler à la capture d'écran ci-dessous.

- -

Form Example: Create Author

- -

Entrez des valeurs pour les champs et ensuite cliquez sur Submit pour sauvegarder l'enregistrement de cet auteur. Vous devriez maintenant être conduit à une vue "détail" pour votre nouvel auteur, avec une URL du genre http://127.0.0.1:8000/catalog/author/10.

- -

Vous pouvez tester l'édition d'un enregistrement en ajoutant /update/ à la fin de l'URL "détail" (par exemple http://127.0.0.1:8000/catalog/author/10/update/). Nous ne mettons pas de capture d'écran, car c'est à peu près la même chose que la page "create".

- -

Enfin, nous pouvons effacer l'enregistrement en ajoutant "delete" à la fin de l'URL de détail (par exemple http://127.0.0.1:8000/catalog/author/10/delete/). Django devrait vous afficher la page de suppression montrée ci-dessous. Cliquez sur "Yes, delete" pour supprimer l'enregistrement et être reconduit à la liste des auteurs.

- -

- -

Mettez-vous au défi

- -

Créez des formulaires pour créer, modifier et effacer des enregistrements de type Book. Vous pouvez utiliser exactement la même structure que pour les Authors. Si votre template book_form.html est simplement copié-renommé à partir du template author_form.html, alors la nouvelle page "create book" va ressembler à quelque chose comme ceci :

- -

- - - -

Résumé

- -

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.

- -

Il y a bien d'autres choses qui peuvent être faites avec les formulaires (regardez notre liste See also ci-dessous), mais vous devez être maintenant en mesure de comprendre comment ajouter des formulaires basiques et un code de gestion de formulaire à vos propres sites web.

- -

See also

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}

diff --git a/files/fr/learn/server-side/django/forms/index.md b/files/fr/learn/server-side/django/forms/index.md new file mode 100644 index 0000000000..2d63644055 --- /dev/null +++ b/files/fr/learn/server-side/django/forms/index.md @@ -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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}
+ +

Dans cette formation nous allons vous montrer comment travailler avec les formulaires HTML sous Django afin de créer, modifier et supprimer des instances de modèle. Pour illustrer le raisonnement, nous allons étendre le site web LocalLibrary 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. 

+ + + + + + + + + + + + +
Prérequis:Avoir terminé les formations précédentes, y compris Django Tutorial Part 8: User authentication and permissions.
Objectifs: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.   
+ +

Vue d'ensemble

+ +

Un formulaire HTML regroupe au moins un champ remplissable et des composants élémentaires d'interface web.  Il peut être utilisé pour réunir des saisies de la part des utilisateurs avant envoi vers un serveur. Les formulaires sont souples: ils s'adaptent à plusieurs modes de saisie. En effet, Il existe des composants élementaires d'interfaces graphique pour des  modes de saisie non contrainte avec une zone de saisie de texte, ou resteinte au type date avec un date picker,  la saisie d'un variable optionnelle via une boîte à cocher,  d'un choix à faire parmi plusieurs valeurs possibles avec les boutons radio etc... . Les formulaires permettent de partager des informations avec le serveur de manière relativement sécurisée  car ils permettent d'envoyer des requêtes de  type POST protégeant de la falsification des requêtes inter-site.

+ +

Bien que nous n'ayons pas encore créé de formulaire au cours de cette formation, nous en avons déjà rencontré sur l'interface d'administration Django Admin — par exemple la capture d'écran ci-dessous montre un formulaire d'édition  de l'un de nos modèle de Book  (livre), comprenant des composants élémentaires d'interface graphique de chois de valeur parmi une liste proposée,  et des zones des saisie de texte.

+ +

Admin Site - Book Add

+ +

Travailler avec des formulaires peut s'avérer compliqué ! Les développeurs doivent non seulement écrire le code  HTML pour le formulaire, mais aussi vérifier et corriger sur le serveur les données saisies (et éventuellement aussi dans le navigateur), renvoyer le formulaire avec des messages d'erreur pour informer les usagers de tout champ invalide, prendre en charge les données quand elles passent l'étape de vérification, et finalement renvoyer une information à l'utilisateur d'une manière ou d'une autre pour indiquer ce succès. Les formulaires sous Django enlèvent beaucoup de travail à chacune de ces étapes, grâce à un cadriciel qui permet de déclarer des formulaires et leurs champs à travers un langage de programmation, puis d'utiliser ces objets non seulement pour générer le code HTML, mais aussi une grosse partie de la vérification des données et du retour d'information à l'utilisateur.

+ +

Dans cette formation, nous allons vous montrer quelque-unes des manièrs de créer et de travailler avec les formulaires, et en particulier, comment les vues sur les formulaires génériques d'édition peuvent réduire significativement la quantité de travail à fournir pour créer les formulaires de manipulation de vos modèles. En chemin nous allons étendre notre application LocalLibrary en ajoutant un formulaire permettant aux bibliothécaires de prolonger le prêt de libres, et nous allons créer des pages pour créer, modifier et supprimer des livres et des auteurs (reproduisant une version basique du formulaire ci-dessus pour éditer des livres. )

+ +

Formulaires HTML

+ +

D'abord, un premier aperçu des formulaires HTML (HTML Forms). Soit un formulaire HTML simple, composé d'un unique champ de saisie texte , présent pour y entrer le nom d'une "équipe" quelconque, et son sa description dans l'étiquette associée :

+ +

Simple name field example in HTML form

+ +

Le formulaire est défini en HTML comme une collection d'éléments enfermés entre deux balises <form> ... </form> contenant au moins une balise <input> dont la valeur d'attribut 'type' doit valoir "submit":

+ +
<form action="/team_name_url/" method="post">
+    <label for="team_name">Enter name: </label>
+    <input id="team_name" type="text" name="name_field" value="Default name for team.">
+    <input type="submit" value="OK">
+</form>
+
+ +

Bien qu'ici nous n'ayons qu'un champ de saisie texte destiné à recevoir le nom d'équipe, une formulaire pourrait avoir un nombre quelconque d'autres champs de saisie et leurs étiquettes de description associées. La valeur de l'attribut 'type' définit la sorte de composant élementaire d'interface graphique  affichée. Les attributs 'id' et 'name' permettent d'identifier le champ en JavaScript/CSS/HTML alors que l'attribut 'value' définit la valeur initiale du champ lorsqu'il est affiché pour la première fois. La description associée  est déclarée par la balise <label> (voir "Enter Name" au dessus) , avec un attribut 'for' devant contenir la valeur de l'attribut 'id' du champ imput à laquelle on souhaite l'associer.  

+ +

La balise <input> dont l'attribut 'type' vaut submit sera affichée (par défaut) comme un bouton  qui peut être cliqué par l'utilisateur pour envoyer vers le serveur les données figurant dans tous les autres éléments de formulaire <input> (dans le cas présent, la valeur actuelle de 'team name'. Les attributs de formulaire déterminent d'une part la méthode HTTP (attribut method) utilisée pour envoyer les donnnées et d'autre part la destination des données sur le serveur (attribut action ):

+ + + +

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 .  

+ +

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!

+ +

Les étapes de gestion d'un formulaire avec Django

+ +

Django gère un formulaire en utilisant les mêmes techniques qu'évoquées lors des formations précédentes (pour afficher des informations à propos de nos modèles): La vue reçoit une requête, exécute toute acion nécessaire, incluant la lecture de données depuis les modèles, puis génère une page HTML (à partir d'un squelette à qui nous transmettons un contexte contenant les données à afficher ). Ce qui rend les choses plus compliquées, c'est que le serveur a aussi besoin d'être capable de traiter les données fournies par l'utilisateur (pas seulement le contexte) et doit pouvoir réafficher les pages s'il y a une quelconque erreur.

+ +

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).

+ +

Updated form handling process doc.

+ +

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 : 

+ +
    +
  1. Afficher le formulaire sous sa forme par défaut la première fois où il est demandé par l'utilisateur. +
      +
    • Le formulaire peut contenir des champs vides (par exemple si vous créez un nouvel enregistrement ) ou peut être prérempli de valeurs initiales (par exemple si vous modifiez les valeurs d'un enregistrement existant, ou que ces champs ont des valeurs initiales utiles ).
    • +
    • Le formulaire est qualifié à cette étape de formulaire libre, parce qu'il n'est associé à aucune donnée entré par l'utilisateur (bien qu'il puisse avoir des valeurs initiales) . 
    • +
    +
  2. +
  3. Recevoir des données d'une requete d'envoi de données et les lier au formulaire. +
      +
    • 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. 
    • +
    +
  4. +
  5. Nettoyer et valider les données +
      +
    • Le nettoyage de données consiste à désinfecter la saisie (par exemple en supprimant les caractères non valides, et qui pourraient être utilisés pour envoyer du contenu malveillant au serveur.) et à convertir ces données en types Python cohérents. 
    • +
    • La validation vérifie que les valeurs envoyées sont appropriées au champ (par exemple dans le bon intervalle de dates, ni trop long ni trop court, etc.) 
    • +
    +
  6. +
  7. 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.
  8. +
  9. 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.)
  10. +
  11. Une fois toutes ces actions accomplies, redirige l'utilisateur vers une autre page.
  12. +
+ +

Django fournit une multitude d'outils et de méthodes pour vous assister dans les tâches mentionnées ci-dessus. Parmi eux la plus importante, la classe Form, qui simplifie à la fois la production de formulaire HTML mais aussi la validation de donnée. In the next section we describe how forms work using the practical example of a page to allow librarians to renew books.

+ +
+

Note : Comprendre l'utilisation de Form vous aidera quand nous parlerons des classes de formulaires de Django plus complexes. 

+
+ +

Formulaire de renouvellement de livre par l'utilisation de vue Form

+ +

Nous allons maintenant créer une page qui permettra aux bibliothécaires de renouveler les livres empruntés (les rendre disponible à nouveau). Pour cela nous allons créer un formulaire qui permet aux utilisateurs de saisir une valeur de type Date. Considérons le champ avec une valeur initiale égale à la date du jour plus 3 semaines (la période normale d'emprunt d'un livre), et ajouter une validation pour s'assurer que le bibliothécaire ne peut pas saisir une date dans le passé ou une date trop éloignée dans le futur. Quand une date valide a été entrée, nous l'enregistrons dans le champ BookInstance.due_back de l'enregistrement courant.

+ +

L'exemple va utiliser une vue basée sur une fonction et une classe Form. Les sections suivantes expliquent comment les formulaires fonctionnent, et les changements que vous devez faire à notre projet en cours LocalLibrary.

+ +

Formulaire

+ +

La classe Form est le cœur du système de gestion des formulaires de Django. Elle spécifie les champs présents dans le formulaire, affiche les widgets, les labels, les valeurs initiales, les valeurs valides et (après validation) les messages d'erreur associés aux champs invalides. Cette classe fournit également des méthodes pour se restituer elle-même dans les templates en utilisant des formats prédéfinis (tables, listes etc.) ou pour obtenir la valeur de chaque élément de formulaire (permettant un rendu manuel fin).

+ +

Déclarer un formulaire

+ +

La syntaxe de déclaration pour un Form est très semblable à celle utilisée pour déclarer un Model, et partage les mêmes types de champs (et des paramètres similaires). Cela est logique, puisque dans les deux cas nous avons besoin de nous assurer que chaque champ gère le bon type de donnée, est contraint lors de la validation des données, et a une description pour l'affichage/la documentation.

+ +

Les données de formulaire sont stockées dans un fichier application forms.py, à l'intérieur du répertoire de l'application. Créez et ouvrez le fichier locallibrary/catalog/forms.py. Pour créer un Form, nous importons la bibliothèque forms, dérivons une classe de la classe Form, et déclarons les champs du formulaire. Une classe très basique de formulaire pour notre formulaire de renouvellement de livre dans notre bibliothèque est montrée ci-dessous :

+ +
from django import forms
+
+class RenewBookForm(forms.Form):
+    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+
+ +

Champs de formulaire

+ +

Dans ce cas, nous avons un unique champ DateField pour entrer la date du renouvellement, qui sera rendue en HTML avec une valeur vide, le label par défaut "Renewal date:", et un texte utilitaire indiquant comment s'en servir : "Enter a date between now and 4 weeks (default 3 weeks)." Comme aucun des autres arguments optionnels ne sont spécifiés, le champ acceptera des dates en utilisant les input_formats suivants : YYYY-MM-DD (2016-11-06), MM/DD/YYYY (02/26/2016), MM/DD/YY (10/25/16), et sera rendu en utilisant le widget par défaut : DateInput.

+ +

Il y a beaucoup d'autres types de champs, que vous reconnaîtrez sans peine en raison de leur ressemblance avec les classes de champs équivalentes pour les modèles : BooleanField, CharField, ChoiceField, TypedChoiceField, DateField, DateTimeField, DecimalField, DurationField, EmailField, FileField, FilePathField, FloatField, ImageField, IntegerField, GenericIPAddressField, MultipleChoiceField, TypedMultipleChoiceField, NullBooleanField, RegexField, SlugField, TimeField, URLField, UUIDField, ComboField, MultiValueField, SplitDateTimeField, ModelMultipleChoiceField, ModelChoiceField​​​​.

+ +

Les arguments communs de la plupart des champs sont listés ci-dessous (ils ont les valeurs les plus communes par défaut) :

+ + + +

Validation

+ +

Django fournit un grand nombre d'endroits pour valider vos données. La façon la plus simple de valider un champ unique est de remplacer la méthode clean_<fieldname>() pour le champ à vérifier. Ainsi, par exemple, nous pouvons vérifier que les valeurs entrées pour le champ renewal_date sont entre maintenant et dans 4 semaines, en implémentant la méthode clean_renewal_date() comme montré ci-après.

+ +

Mettez à jour votre fichier forms.py, de telle sorte qu'il ressemble à cela :

+ +
from django import forms
+
+from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
+import datetime #for checking renewal date range.
+
+class RenewBookForm(forms.Form):
+    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 date is not in past.
+        if data < datetime.date.today():
+            raise ValidationError(_('Invalid date - renewal in past'))
+
+        #Check date is in range librarian allowed to change (+4 weeks).
+        if data > 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
+ +

Il y a deux choses importantes à noter. La première est que nous accédons à nos données en utilisant self.cleaned_data['renewal_date'], et que nous retournons ces données, que nous les ayons changées ou non, à la fin de la fonction. Cette étape nous donne des données "nettoyées", purgées de valeurs potentiellement dangereuses en utilisant les validateurs par défaut, et converties en type standard correct pour les données considérées (dans ce cas un objet Python datetime.datetime).

+ +

Le deuxième point est que, si une valeur tombe en dehors de l'intervalle que nous avons autorisé, nous levons une ValidationError, en spécifiant le texte d'erreur que nous voulons afficher dans la zone du formulaire prévue pour le cas où l'utilisateur entre une valeur incorrecte. L'exemple ci-dessus enveloppe aussi ce texte dans ugettext_lazy() (importée comme _()), une des fonctions de traduction Django, ce qui est une bonne pratique si vous voulez traduire votre site plus tard.

+ +
+

Note : Il y a un grand nombre d'autres méthodes et exemples au sujet de la validation des formulaires dans Form and field validation (Django Docs). Par exemple, au cas où vous avez plusieurs champs dépendants les uns des autres, vous pouvez réécrire la fonction Form.clean(), et lever de nouveau une ValidationError.

+
+ +

C'est tout ce dont nous avons besoin pour notre formulaire dans cet exemple.

+ +

Configuration d'URL

+ +

Avant de créer notre vue, ajoutons une configuration d'URL pour la page renew-books. Copiez la configuration suivante à la fin de locallibrary/catalog/urls.py.

+ +
urlpatterns += [
+ url(r'^book/(?P<pk>[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'),
+]
+ +

La configuration d'URL va rediriger les URLs ayant le format /catalog/book/<bookinstance id>/renew/ vers la fonction appelée renew_book_librarian() dans views.py, et envoyer l'id de BookInstance comme paramètre sous le nom pk. Le pattern ne fonctionnera que si pk est un uuid correctement formaté.

+ +
+

Note : Nous pouvons appeler comme bon nous semble la donnée d'URL "pk" que nous avons capturée, car nous contrôlons complètement la fonction de notre view (nous n'utilisons pas une vue générique detail, laquelle attendrait des paramètres avec un certain nom). Cependant, le raccourci pk, pour "primary key", est une convention qu'il est raisonnable d'utiliser !

+
+ +

Vue

+ +

Comme nous l'avons expliqué ci-dessus dans Django form handling process, la vue doit retourner le formulaire par défaut s'il est appelé pour la première fois, et ensuite soit le retourner à nouveau avec les messages d'erreur si les données sont invalides, soit gérer les données et rediriger vers une nouvelle page si elles sont valides. Pour effectuer ces différentes actions, la vue doit être en mesure de savoir si elle est appelée pour la première fois (et retourner le formulaire par défaut) ou pour la deuxième fois ou plus (et valider les données).

+ +

Pour les formulaires qui utilisent une requête POST pour envoyer une information au serveur, la manière la plus commune de procéder pour la vue est de tester le type de requête POST (if request.method == 'POST':) pour repérer des requêtes de type validation de formulaire, et GET (en utilisant une condition else) pour identifer une requête initiale de création de formulaire. Si vous voulez utiliser une requête GET pour envoyer vos données, alors une approche classique pour savoir si la vue est invoquée pour la première fois ou non est de lire les données du formulaire (p. ex. lire une valeur cachée dans le formulaire).

+ +

Le processus de renouvellement de livre va écrire dans notre base de données, aussi, par convention, nous utiliserons le type de requête POST. Le bout de code ci-dessous montre le procédé (très classique) pour cette sorte de vue basée sur des fonctions.

+ +
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
+    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})
+ +

Nous importons tout d'abord notre formulaire (RenewBookForm) et un certain nombre d'autres objets/méthodes utiles, dont nous nous servons dans le corps de la fonction de notre vue :

+ + + +

Dans la vue, nous utilisons d'abord l'argument pk dans la fonction get_object_or_404(), afin d'obtenir la BookInstance courante (si cette instance n'existe pas, la vue se termine immédiatement et la page va afficher une erreur ). Si ce n'est pas une requête POST (cas géré par la clause else), alors nous créons le formulaire par défaut en lui passant une valeur initial pour le champ renewal_date (comme montré en gras ci-dessous, c'est la date actuelle plus 3 semaines).

+ +
 book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # 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})
+ +

Après création du formulaire, nous appelons la fonction render() pour créer la page HTML, en précisant le template et un contexte qui contient notre formulaire. Dans ce cas, le contexte contient aussi notre BookInstance, que nous allons utiliser dans le template pour fournir des informations à propos du livre que nous sommes en train de renouveler.

+ +

En revanche, s'il s'agit d'une requête POST, alors nous créons notre objet form et le peuplons avec des données récupérées dans la requête. Ce processus est appelé "binding" (liaison) et nous permet de valider le formulaire. Ensuite nous vérifions que le formulaire est valide, ce qui déclenche tout le code de validation sur tous les champs - ce qui inclut à la fois le code générique vérifiant que notre champ date est effectivement une date valide, et notre fonction clean_renewal_date(), spécifique à notre formulaire, pour vérifier que la date est dans le bon intervalle.

+ +
    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') )
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
+ +

Si le formulaire n'est pas valide, nous appelons aussi la fonction render(), mais cette fois les valeurs passées dans le contexte vont inclure les messages d'erreur.

+ +

Si le formulaire est valide, alors nous pouvons commencer à utiliser les données, en y accédant à travers l'attribut form.cleaned_data (p. ex. data = form.cleaned_data['renewal_date']). Ici nous ne faisons que sauvegarder dans la valeur due_back de l'objet BookInstance associé les données reçues.

+ +
+

Attention : Alors que vous pouvez accéder aussi aux données de formulaire directement à travers la requête (par exemple request.POST['renewal_date'], ou request.GET['renewal_date'] si vous utilisez une requête GET), ce n'est PAS recommandé. Les données nettoyées sont assainies, validées et converties en types standard Python.

+
+ +

L'étape finale dans la partie "gestion de formulaire" de la vue est de rediriger vers une autre page, habituellement une page "success". Dans ce cas, nous utilisons HttpResponseRedirect et reverse() pour rediriger vers la vue appelée 'all-borrowed' (qui a été créée dans la partie "challenge" de Django Tutorial Part 8: User authentication and permissions. Si vous n'avez pas créé cette page, vous pouvez rediriger vers la page d'accueil à l'URL '/').

+ +

C'est tout ce qui est requis pour la gestion du formulaire lui-même, mais il nous faut encore restreindre l'accès à la vue aux seuls libraires. Nous devrions sans doute créer un nouveau réglage de permission dans BookInstance ("can_renew"), mais, pour ne pour ne pas compliquer les choses ici, nous allons simplement utiliser le décorateur de fonction @permission_required avec notre permission existante can_mark_returned.

+ +

Le résultat final de la vue est donc comme indiqué ci-dessous. Veuillez copier ceci en bas de locallibrary/catalog/views.py.

+ +
from django.contrib.auth.decorators import permission_required
+
+from django.shortcuts import get_object_or_404
+from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+import datetime
+
+from .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_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})
+
+ +

Le template

+ +

Créez le template référencé dans la vue (/catalog/templates/catalog/book_renew_librarian.html) et copiez-y le code suivant :

+ +
{% extends "base_generic.html" %}
+{% block content %}
+
+ <h1>Renew: \{{bookinst.book.title}}</h1>
+ <p>Borrower: \{{bookinst.borrower}}</p>
+ <p{% if bookinst.is_overdue %} class="text-danger"{% endif %}>Due date: \{{bookinst.due_back}}</p>
+
+ <form action="" method="post">
+ {% csrf_token %}
+ <table>
+ \{{ form }}
+  </table>
+ <input type="submit" value="Submit" />
+ </form>
+
+{% endblock %}
+ +

La majeure partie de ce code devrait vous être familière si vous avez suivi les tutoriels précédents. Nous étendons le template de base et ensuite redéfinissons le block "content". Nous sommes en mesure de référencer \{{ book_instance }} (et ses variables), puisqu'il a été passé dans l'objet context par la fonction render(), et nous utilisons tout cela pour lister le titre du livre, son emprunteur et la date originale de retour.

+ +

Le code du formulaire est relativement simple. Nous déclarons d'abord les tags form, en précisant où le formulaire doit être adressé (action) et la method utilisée pour soumettre les donées (ici un "HTTP POST"). Si vous vous rappelez ce qui a été dit en haut de cette page (aperçu sur les HTML Forms), une action vide comme ici signifie que les données de formulaire seront postées à nouveau à l'URL actuelle (ce qui est le comportement que nous voulons !). À l'intérieur des tags, nous définissons le bouton , sur lequel l'utilisateur peut appuyer pour envoyer les données. Le {% csrf_token %} ajouté juste à l'intérieur des tags "form" est un des éléments de protection utilisés par Django contre les "cross-site forgery".

+ +
+

Note : Ajoutez le {% csrf_token %} à tout template Django que vous créeez et qui utilise POST pour soumettre les données. Cela réduira les risques qu'un utilisateur mal intentionné pirate vos formulaires.

+
+ +

Tout ce qui reste est la variable de template \{{ form }}, que nous avons passée au template dans le dictionnaire de contexte. Peut-être sans surprise, quand il est utilisé comme indiqué, il fournit le rendu par défaut de tous les champs de formulaire, y compris leurs labels, widgets et textes d'aide. Voici le rendu :

+ +
<tr>
+  <th><label for="id_renewal_date">Renewal date:</label></th>
+  <td>
+  <input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required />
+  <br />
+  <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
+  </td>
+</tr>
+
+ +
+

Note : Ce n'est peut-être pas évident, car nous n'avons qu'un seul champ, mais, par défaut, chaque champ est défini dans sa propre ligne de tableau. Ce même rendu est fourni si vous référencez la variable de template \{{ form.as_table }}.

+
+ +

Si vous aviez entré une date invalide, vous obtiendriez en plus sur la page une liste des erreurs (indiquées en gras ci-dessous).

+ +
<tr>
+  <th><label for="id_renewal_date">Renewal date:</label></th>
+  <td>
+  <ul class="errorlist">
+  <li>Invalid date - renewal in past</li>
+  </ul>
+  <input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required />
+  <br />
+  <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span>
+ </td>
+</tr>
+ +

Autres façons d'utiliser la variable de template du formulaire

+ +

Si vous utilisez \{{ form.as_table }} comme indiqué ci-dessus, chaque champ est rendu comme une ligne de tableau. Vous pouvez également rendre chaque champ comme un élément de liste (en utilisant \{{ form.as_ul }}) ou comme un paragraphe (en utilisant \{{ form.as_p }}).

+ +

Il est également possible d'avoir un contrôle complet sur le rendu de chaque partie du formulaire, en indexant ses propriétés grâce à la notation pointée. Ainsi, par exemple, nous pouvons accéder un certain nombre d'éléments distincts pour notre champ renewal_date :

+ + + +

Pour plus d'exemples sur la manière de rendre manuellement des formulaires dans des templates, et boucler de manière dynamique sur les champs du template, voyez Working with forms > Rendering fields manually (Django docs).

+ +

Tester la page

+ +

Si vous avez accepté le "challenge" dans Django Tutorial Part 8: User authentication and permissions, 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.

+ +
{% if perms.catalog.can_mark_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a> {% endif %}
+ +
+

Note : Souvenez-vous que votre login de test devra avoir la permission "catalog.can_mark_returned" pour pouvoir accéder la page de renouvellement de livre (utilisez peut-être votre compte superuser).

+
+ +

Vous pouvez aussi construire manuellement une URL de test comme ceci : http://127.0.0.1:8000/catalog/book/<bookinstance_id>/renew/ (un id de bookinstance valide peut être obtenu en navigant vers une page de détail de livre dans votre bibliothèque, et en copiant le champ id).

+ +

À quoi cela ressemble-t-il ?

+ +

Si tout a bien marché, le formulaire par défaut ressemblera à ceci :

+ +

+ +

Le formulaire avec valeur erronée ressemblera à ceci :

+ +

+ +

La liste de tous les livres avec les liens vers le renouvellement ressemblera à ceci :

+ +

+ +

ModelForms

+ +

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).

+ +

Cependant, si vous avez seulement besoin d'un formulaire qui répertorie les champs d'un modèle unique, alors votre modèle définira déjà la plupart des informations requises dans votre formulaire : champs, labels, texte d'aide etc. Plutôt que de créer à nouveau les définitions du modèle dans votre formulaire, il est plus facile d'utiliser la classe d'aide ModelForm pour créer le formulaire d'après votre modèle. Ce ModelForm peut dès lors être utilisé à l'intérieur de vos vues exactement de la même manière qu'un Form ordinaire.

+ +

Un ModelForm basique, contenant le même champ que notre RenewBookForm d'origine, est montré ci-dessous. Tout ce que vous avez à faire pour créer le formulaire, c'est ajouter class Meta avec le model (BookInstance) associé, et une liste des fields du modèle à inclure dans le formulaire (vous pouvez inclure tous les champs en utilisant fields = '__all__', ou bien utiliser exclude (au lieu de fields) pour préciser les champs à ne pas importer du modèle).

+ +
from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+ class Meta:
+ model = BookInstance
+ fields = ['due_back',]
+
+ +
+

Note : Cela peut ne pas sembler beaucoup plus simple que d'utiliser un simple Form, et ça ne l'est effectivement pas dans ce cas, parce que nous n'avons qu'un seul champ. Cependant, si vous avez beaucoup de champs, cela peut réduire notablement la quantité de code !

+
+ +

Le reste de l'information vient des définitions de champ données par le modèle (par ex. les labels, les widgets, le texte d'aide, les messages d'erreur). S'ils ne sont pas suffisamment satisfaisants, nous pouvons les réécrire dans notre class Meta, en précisant un dictionnaire contenant le champ à modifier et sa nouvelle valeur. Par exemple, dans ce formulaire, nous pourrions souhaiter, pour notre champ, un label tel que "Renewal date" (plutôt que celui par défaut, basé sur le nom du champ : Due Back), et nous voulons aussi que notre texte d'aide soit spécifique à ce cas d'utilisation. La classe Meta ci-dessous vous montre comment réécrire ces champs, et vous pouvez pareillement définir widgets et error_messages si les valeurs par défaut ne sont pas suffisantes.

+ +
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).'), } 
+
+ +

Pour ajouter une validation, vous pouvez utiliser la même approche que pour un Form normal : vous définissez une fonction appelée clean_field_name(), et vous levez des exceptions de type ValidationError pour les valeurs non valides. La seule différence par rapport à notre formulaire original, c'est que le champ de modèle est appelé due_back et non "renewal_date". Ce changement est nécessaire, dans la mesure où le champ correspondant dans BookInstance est appelé due_back.

+ +
from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+    def clean_due_back(self):
+       data = self.cleaned_data['due_back']
+
+  #Check date is not in past.
+       if data < datetime.date.today():
+           raise ValidationError(_('Invalid date - renewal in past'))
+
+       #Check date is in range librarian allowed to change (+4 weeks)
+       if data > 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
+
+ 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).'), }
+
+ +

La classe RenewBookModelForm ci-dessus est maintenant fonctionnellement équivalente à notre RenewBookForm d'origine. Vous pourriez l'importer et l'utiliser partout où vous utilisez RenewBookForm, du moment que vous changez aussi de renewal_date en due_back le nom de variable du formulaire correspondant, comme dans la deuxième déclaration du formulaire : RenewBookModelForm(initial={'due_back': proposed_renewal_date}.

+ +

Vues génériques d'édition

+ +

L'algorithme de gestion des formulaires que nous avons utilisé ci-dessus dans notre exemple de vue basée sur une fonction, représente un processus extrêmement commun dans vues destinées à éditer un formulaire. Django abstrait pour vous la plus grande partie de ce processus répétitif ("boilerplate") en proposant des generic editing views pour les vues de création, éditition et suppression basées sur des modèles. Ces vues génériques non seulement assument le comportement d'une vue, mais elles créent automatiquement la classe de formulaire (un ModelForm) pour vous à partir du modèle.

+ +
+

Note : En plus des vues d'édition décrites ici, il existe aussi une classe FormView, qui se tient, en termes de rapport "flexibilité"/"effort codage", à mi-chemin entre notre vue basée sur une fonction et les autres vues génériques. En utilisant FormView, vous avez encore besoin de créer votre Form, mais vous n'avez pas besoin d'implémenter tous les éléments d'une gestion standard de formulaire. À la place, vous n'avez qu'à fournir une implémentation de la fonction qui sera appelée une fois que les données envoyées sont reconnues valides.

+
+ +

Dans cette section, nous allons utiliser des vues génériques d'édition pour créer des pages afin de pouvoir ajouter les fonctionnalités de création, d'édition et de suppression des enregistrements de type Author de notre bibliothèque, en fournissant efficacement une réimplémentation basique de certaines parties du site Admin (cela peut être intéressant si vous avez besoin d'offrir une fonctionnalité admin d'une manière plus flexible que ce qui peut être présenté par le site admin).

+ +

Vues

+ +

Ouvrez le fichier vue (locallibrary/catalog/views.py) et ajoutez le bloc de code suivant à la fin :

+ +
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')
+ +

Comme vous pouvez le voir, pour les vues "créer", "modifier" et "supprimer", vous avez besoin de dériver respectivement des vues génériques CreateView, UpdateView, et DeleteView, et de définir ensuite le modèle associé.

+ +

Pour les cas "créer" et "modifier", vous devez aussi préciser les champs à afficher dans le formulaire (en utilisant la même syntaxe que pour la classe ModelForm). Dans ce cas, nous montrons à la fois la syntaxe pour afficher "tous" les champs, et comment vous pouvez les lister un par un. Vous pouvez aussi spécifier les valeurs initiales pour chacun des champs, en utilisant un dictionnaire de paires nom_du_champ/valeur (ici nous définissons arbitrairement la date de mort, uniquement dans un but de démonstration - sans doute voudrez-vous l'enlever !). Par défaut, ces vues vont rediriger en cas de succès vers une page affichant l'élément nouvellement créé ou modifié, ce qui, dans notre cas, sera la vue "détail" d'un auteur, créée dans un précédent tutoriel. Vous pouvez spécifier un autre lieu de redirection en déclarant explicitement le paramètre success_url (comme indiqué dans la classe AuthorDelete).

+ +

La classe  ne requiert pas l'affichage d'aucun champ, aussi n'ont-ils pas besoin d'être précisés. Par contre il vous faut bien spécifier la success_url, car Django n'a pas de valeur par défaut pour cela. Dans ce cas, nous utilisons la fonction  pour rediriger vers notre liste d'auteurs après qu'un auteur ait été supprimé. reverse_lazy() est une version de reverse() exécutée mollement ("lazily"), que nous utilisons ici parce que nous fournissons une URL à un attribut de vue basée sur classe.

+ +

Templates

+ +

Les vues "créer" et "modifier" utilisent le même template par défaut, lequel sera nommé d'après votre modèle : model_name_form.html (vous pouvez changer le suffixe en autre chose que _form en utilisant le champ template_name_suffix dans votre vue, par exemple template_name_suffix = '_other_suffix').

+ +

Créez le fichier de template locallibrary/catalog/templates/catalog/author_form.html, et copiez-y le texte suivant.

+ +
{% extends "base_generic.html" %}
+
+{% block content %}
+
+<form action="" method="post">
+ {% csrf_token %}
+ <table>
+ \{{ form.as_table }}
+ </table>
+ <input type="submit" value="Submit" />
+
+</form>
+{% endblock %}
+ +

Ce formulaire est semblable à nos formulaires précédents et affiche les champs en utilisant un tableau. Notez aussi comment nous déclarons à nouveau le {% csrf_token %} pour nous assurer que nos formulaires résisteront à d'éventuelles attaques par CSRF (Cross Site Request Forgery).

+ +

La vue "supprimer" s'attend à trouver un template avec un nom au format model_name_confirm_delete.html (de nouveau, vous pouvez changer le suffixe en utilisant template_name_suffix dans votre vue). Créez le fichier de template locallibrary/catalog/templates/catalog/author_confirm_delete.html, et copiez-y le texte suivant.

+ +
{% extends "base_generic.html" %}
+
+{% block content %}
+
+<h1>Delete Author</h1>
+
+<p>Are you sure you want to delete the author: \{{ author }}?</p>
+
+<form action="" method="POST">
+ {% csrf_token %}
+ <input type="submit" action="" value="Yes, delete." />
+</form>
+
+{% endblock %}
+
+ +

Configurations d'URL

+ +

Ouvrez votre fichier de configuration d'URL (locallibrary/catalog/urls.py) et ajoutez-y à la fin la configuration suivante :

+ +
urlpatterns += [
+ url(r'^author/create/$', views.AuthorCreate.as_view(), name='author_create'),
+ url(r'^author/(?P<pk>\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'),
+ url(r'^author/(?P<pk>\d+)/delete/$', views.AuthorDelete.as_view(), name='author_delete'),
+]
+ +

Il n'y a rien de particulièrement nouveau ici ! Vous pouvez voir que les vues sont des classes, et doivent dès lors être appelée via .as_view(), et vous devriez être capable de reconnaître les patterns d'URL dans chaque cas. Nous devons utiliser pk comme nom pour la valeur de nos clés primaires capturées, car c'est le nom de paramètre attendu par les classes de vue.

+ +

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).

+ +
+

Note : Les utilisateurs observateurs auront remarqué que nous n'avons rien fait pour empêcher les utilisateurs non autorisés d'accéder ces pages ! Nous laissons cela comme exercice pour vous (suggestion : vous pourriez utiliser le PermissionRequiredMixin, et soit créer une nouvelle permission, soit réutiliser notre permissioncan_mark_returned ).

+
+ +

Test de la page

+ +

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.

+ +

Ensuite naviguez à la page de création d'auteur : http://127.0.0.1:8000/catalog/author/create/, ce qui devrait ressembler à la capture d'écran ci-dessous.

+ +

Form Example: Create Author

+ +

Entrez des valeurs pour les champs et ensuite cliquez sur Submit pour sauvegarder l'enregistrement de cet auteur. Vous devriez maintenant être conduit à une vue "détail" pour votre nouvel auteur, avec une URL du genre http://127.0.0.1:8000/catalog/author/10.

+ +

Vous pouvez tester l'édition d'un enregistrement en ajoutant /update/ à la fin de l'URL "détail" (par exemple http://127.0.0.1:8000/catalog/author/10/update/). Nous ne mettons pas de capture d'écran, car c'est à peu près la même chose que la page "create".

+ +

Enfin, nous pouvons effacer l'enregistrement en ajoutant "delete" à la fin de l'URL de détail (par exemple http://127.0.0.1:8000/catalog/author/10/delete/). Django devrait vous afficher la page de suppression montrée ci-dessous. Cliquez sur "Yes, delete" pour supprimer l'enregistrement et être reconduit à la liste des auteurs.

+ +

+ +

Mettez-vous au défi

+ +

Créez des formulaires pour créer, modifier et effacer des enregistrements de type Book. Vous pouvez utiliser exactement la même structure que pour les Authors. Si votre template book_form.html est simplement copié-renommé à partir du template author_form.html, alors la nouvelle page "create book" va ressembler à quelque chose comme ceci :

+ +

+ + + +

Résumé

+ +

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.

+ +

Il y a bien d'autres choses qui peuvent être faites avec les formulaires (regardez notre liste See also ci-dessous), mais vous devez être maintenant en mesure de comprendre comment ajouter des formulaires basiques et un code de gestion de formulaire à vos propres sites web.

+ +

See also

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/authentication_and_sessions", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}

diff --git a/files/fr/learn/server-side/django/generic_views/index.html b/files/fr/learn/server-side/django/generic_views/index.html deleted file mode 100644 index 8917b2d6fc..0000000000 --- a/files/fr/learn/server-side/django/generic_views/index.html +++ /dev/null @@ -1,635 +0,0 @@ ---- -title: 'Tutoriel Django - 6e partie : Vues génériques pour les listes et les détails' -slug: Learn/Server-side/Django/Generic_views -tags: - - Beginner - - Learn - - Tutorial - - django - - django templates - - django views -translation_of: Learn/Server-side/Django/Generic_views -original_slug: Learn/Server-side/Django/Vues_generiques ---- -
- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}
- -
Ce tutoriel améliore notre site web LocalLibrary, 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.
- - - - - - - - - - - - -
Prérequis: -

Avoir terminé tous les tutoriels précédents, y compris Django Tutorial Part 5: Creating our home page.

-
Objectif: -

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.

-
- -

Aperçu

- -

Dans ce tutoriel, nous allons terminer la première version du site web LocalLibrary, 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 !).

- -

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.

- -

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.

- -

Page de liste de livres

- -

La page de liste des livres va afficher une liste de tous les enregistrements de livres disponibles, en utilisant l'URL: catalog/books/. 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 (base_generic.html) que nous avons créé dans le tutoriel précédent.

- -

Mappage d'URL

- -

Ouvrez le fichier /catalog/urls.py, et copiez-y la ligne en gras ci-dessous. Comme pour la page d'index, cette fonction path() définit un pattern destiné à identifier l'URL ('books/'), une fonction vue qui sera appelée si l'URL correspond (views.BookListView.as_view()), et un nom pour ce mappage particulier.

- -
urlpatterns = [
-    path('', views.index, name='index'),
-    path('books/', views.BookListView.as_view(), name='books'),
-]
- -

Comme discuté dans le tutoriel précédent, l'URL doit auparavant avoir identifié la chaîne /catalog, aussi la vue ne sera réellement appelée que pour l'URL complète: /catalog/books/.

- -

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.

- -

En Django, on accède à la fonction appropriée d'une vue basée sur classe en appelant sa méthode de classe as_view(). 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.

- -

Vue (basée sur classe)

- -

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 render() 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 (ListView), 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.

- -

Ouvrez le fichier catalog/views.py, et copiez-y le code suivant à la fin :

- -
from django.views import generic
-
-class BookListView(generic.ListView):
-    model = Book
- -

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é (Book), et ensuite rendre un template situé à l'adresse /locallibrary/catalog/templates/catalog/book_list.html (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 object_list OU book_list (c'est-à-dire l'appellation générique "the_model_name_list").

- -
-

Note : Ce chemin étrange vers le lieu du template n'est pas une faute de frappe : les vues génériques cherchent les templates dans /application_name/the_model_name_list.html (catalog/book_list.html dans ce cas) à l'intérieur du répertoire /application_name/templates/ (/catalog/templates/).

-
- -

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.

- -
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
- -

Ré-écrire des méthodes dans des vues basées sur classes

- -

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.

- -

Par exemple, nous pouvons ré-écrire la méthode get_queryset() pour changer la liste des enregistrements retournés. Cette façon de faire est plus flexible que simplement définir l'attribut queryset, 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) :

- -
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
-
- -

Nous pourrions aussi réécrire get_context_data(), 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 "some_data" au contexte (elle sera alors accessible comme variable de template).

- -
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
- -

Quand vous faites cela, il est important de suivre la procédure indiquée ci-dessus :

- - - -
-

Note : Voyez dans Built-in class-based generic views (doc de Django) pour avoir beaucoup plus d'exemples de ce que vous pouvez faire.

-
- -

Créer le template pour la Vue Liste

- -

Créez le fichier HTML /locallibrary/catalog/templates/catalog/book_list.html, 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é Book, dans une application appelée catalog).

- -

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 index, nous étendons notre template de base à la première ligne, et remplaçons ensuite le bloc appelé content.

- -
{% extends "base_generic.html" %}
-
-{% block content %}
-  <h1>Book List</h1>
-  {% if book_list %}
-  <ul>
-    {% for book in book_list %}
-      <li>
-        <a href="\{{ book.get_absolute_url }}">\{{ book.title }}</a> (\{{book.author}})
-      </li>
-    {% endfor %}
-  </ul>
-  {% else %}
-    <p>There are no books in the library.</p>
-  {% endif %} 
-{% endblock %}
- -

La vue envoie le contexte (liste de livres), en utilisant par défaut les alias object_list et book_list ; l'un et l'autre fonctionnent.

- -

Exécution conditionnelle

- -

Nous utilisons les balises de templates if, else, et endif pour vérifier que la book_list a été définie et n'est pas vide. Si book_list est vide, alors la condition else affiche un texte expliquant qu'il n'y a pas de livres à lister. Si book_list n'est pas vide, nous parcourons la liste de livres.

- -
{% if book_list %}
-  <!-- code here to list the books -->
-{% else %}
-  <p>There are no books in the library.</p>
-{% endif %}
-
- -

La condition ci-dessus ne vérifie qu'un seul cas, mais vous pouvez ajouter d'autres tests grâce à la balise de template elif (par exemple {% elif var2 %}). Pour plus d'information sur les opérateurs conditionnels, voyez ici :  if, ifequal/ifnotequal, et ifchanged dans Built-in template tags and filters (Django Docs).

- -

Boucles for

- -

Le template utilise les balises de template for et endfor pour boucler à travers la liste de livres, comme montré ci-dessous. Chaque itération peuple la variable de template book avec l'information concernant l'élément courant de la liste.

- -
{% for book in book_list %}
-  <li> <!-- code here get information from each book item --> </li>
-{% endfor %}
-
- -

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 forloop.last pour réaliser une action conditionnelle au dernier passage de la boucle.

- -

Accéder aux variables

- -

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.

- -
<a href="\{{ book.get_absolute_url }}">\{{ book.title }}</a> (\{{book.author}})
-
- -

Nous accédont aux champs de l'enregistrement "livre" associé, en utilisant la notation "à points" (par exemple book.title et book.author), où le texte suivant l'item book est le nom du champ (comme défini dans le modèle).

- -

Nous pouvons aussi appeler des fonctions contenues dans le modèle depuis l'intérieur de notre template — dans ce cas nous appelons Book.get_absolute_url() 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 !).

- -
-

Note : 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 !

-
- -

Mettre à jour le template de base

- -

Ouvrez le template de base (/locallibrary/catalog/templates/base_generic.html) et insérez {% url 'books' %} dans le lien URL pour All books, 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").

- -
<li><a href="{% url 'index' %}">Home</a></li>
-<li><a href="{% url 'books' %}">All books</a></li>
-<li><a href="">All authors</a></li>
- -

À quoi cela ressemble-t-il ?

- -

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.

- -

Page de détail d'un livre

- -

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 catalog/book/<id> (où <id> est la clé primaire pour le livre). En plus des champs définis dans le modèle Book (auteur, résumé, ISBN, langue et genre), nous allons aussi lister les détails des copies disponibles (BookInstances), 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.

- -

Mappage d'URL

- -

Ouvrez /catalog/urls.py et ajoutez-y le mappeur d'URL 'book-detail' indiqué en gras ci-dessous. Cette fonction path() définit un pattern, la vue générique basée sur classe qui lui est associée, ainsi qu'un nom.

- -
urlpatterns = [
-    path('', views.index, name='index'),
-    path('books/', views.BookListView.as_view(), name='books'),
-    path('book/<int:pk>', views.BookDetailView.as_view(), name='book-detail'),
-]
- -

Pour le chemin book-detail, 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 ('<' et '>') 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, <something>  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 spécification de convertisseur, qui définit le type de la donnée (int, str, slug, uuid, path).

- -

Dans ce cas, nous utilisons '<int:pk>' 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é pk (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.

- -
-

Note : Comme nous l'avons dit précédemment, notre URL correcte est en réalité catalog/book/<digits> (comme nous sommes dans l'application catalog/catalog/ est supposé).

-
- -
-

Attention : La vue générique basée sur classe "détail" s'attend à recevoir un paramètre appelé pk. 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é.

-
- -

Introduction aux chemins et expressions régulières avancés

- -
-

Note : 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.

-
- -

La recherche de pattern fournie par path() est simple et utile pour les cas (très communs) où vous voulez seulement capturer n'importe quelle 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 re_path().

- -

Cette méthode est utilisée exactement comme path(), sauf qu'elle vous permet de spécifier un pattern utilisant une Expression régulière. Par exemple, le chemin précédent pourrait avoir été écrit ainsi :

- -
re_path(r'^book/(?P<pk>\d+)$', views.BookDetailView.as_view(), name='book-detail'),
-
- -

Les expressions régulières 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 !

- -

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 : r'<votre texte d'expression régulière va ici>').

- -

L'essentiel de ce que vous aurez besoin de savoir pour déclarer une recherche de pattern est contenu dans le tableau qui suit :

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SymbolMeaning
^Recherche le début du texte.
$Recherche la fin du texte.
\dRecherche un digit (0, 1, 2, ... 9).
\wRecherche un caractère de mot, c'est-à-dire tout caractère dans l'alphabet (majuscule ou minuscule), un digit ou un underscore (_).
+Recherche au moins une occurence du caractère précédent. Par exemple, pour rechercher au moins 1 digit, vous utiliseriez \d+. Pour rechercher au moins 1 caractère "a", vous utiliseriez a+.
*Recherche zéro ou plus occurrence(s) du caractère précédent. Par exemple, pour rechercher "rien ou un mot", vous pourriez utiliser \w*.
( )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).
(?P<name>...)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 !
[  ]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.
- -

La plupart des autres caractères peuvent être pris littéralement.

- -

Considérons quelques exemples réels de patterns :

- - - - - - - - - - - - - - - - - - - - - - -
PatternDescription
r'^book/(?P<pk>\d+)$' -

C'est là l'expression régulière utilisée dans notre mappeur d'URL. Elle recherche une chaîne qui a book/ au commencement de la ligne (^book/), ensuite a au moins 1 digit (\d+), et enfin se termine (avec aucun caractère non-digit avant la fin du marqueur de ligne).

- -

Elle capture aussi tous les digits (?P<pk>\d+) et les passe à la vue dans un paramètre appelé 'pk'. Les valeurs capturées sont toujours passées comme des chaînes !

- -

Par exemple, cette expression régulière trouverait une correspondance dans l'URL book/1234, et enverrait alors une variable pk='1234' à la vue.

-
r'^book/(\d+)$' -

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é.

-
r'^book/(?P<stub>[-\w]+)$' -

Ceci recherche une chaîne qui a book/ au commencement de la ligne (^book/), ensuite a au moins 1 caractère étant soit un '-', soit un caractère de mot ([-\w]+), puis la fin. Ce pattern capture aussi cet ensemble de caractères et le passe à la vue en tant que paramètre nommé 'stub'.

- -

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 /catalog/book/the-secret-garden, plutôt que /catalog/book/33.

-
- -

Vous pouvez capturer plusieurs patterns en une seule fois, et donc encoder beaucoup d'informations différentes dans l'URL.

- -
-

Note : 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.

-
- -

Passer des options supplémentaires dans vos mappages d'URL

- -

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 options supplémentaires. Les options sont déclarées comme un dictionnaire que vous passez comme troisième argument (non nommé) à la fonction path(). 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).

- -
path('url/', views.my_reused_view, {'my_template_name': 'some_path'}, name='aurl'),
-path('anotherurl/', views.my_reused_view, {'my_template_name': 'another_path'}, name='anotherurl'),
-
- -
-

Note : Les options supplémentaires aussi bien que les patterns capturés sont passés à la vue comme arguments nommés. Si vous utilisez le même nom 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).

-
- -

Vue (basée sur classe)

- -

Ouvrez catalog/views.py, et copiez-y le code suivant à la fin du fichier :

- -
class BookDetailView(generic.DetailView):
-    model = Book
- -

C'est tout ! La seule chose que vous avez à faire maintenant, c'est créer un template appelé /locallibrary/catalog/templates/catalog/book_detail.html, et la vue va lui passer les informations de la base de donnée concernant l'enregistrement Book 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 object OU book (c'est-à-dire, de manière générique, "le_nom_du_modèle").

- -

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.

- -

Que se passe-t-il si l'enregistrement n'existe pas ?

- -

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 Http404 — en production, cela va automatiquement afficher une page appropriée "ressource non trouvée", que vous pouvez personnaliser si besoin.

- -

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 pas la vue générique basée sur classe "détail".

- -
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})
-
- -

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 Http404 pour indiquer que le livre est "non trouvé". L'étape finale est ensuite, comme d'habitude, d'appeler render() avec le nom du template et les données concernant le livre dans le paramètre context (comme un dictionnaire).

- -

Une alternative consiste à utiliser la fonction get_object_or_404() comme un raccourci pour lever une exception Http404 si l'enregistrement n'existe pas.

- -
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})
- -

Créerle template de la Vue Détail

- -

Créez le fichier HTML /locallibrary/catalog/templates/catalog/book_detail.html, 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 detail (pour un modèle appelé Book dans une application appelée catalog).

- -
{% extends "base_generic.html" %}
-
-{% block content %}
-  <h1>Title: \{{ book.title }}</h1>
-
-  <p><strong>Author:</strong> <a href="">\{{ book.author }}</a></p> <!-- author detail link not yet defined -->
-  <p><strong>Summary:</strong> \{{ book.summary }}</p>
-  <p><strong>ISBN:</strong> \{{ book.isbn }}</p>
-  <p><strong>Language:</strong> \{{ book.language }}</p>
-  <p><strong>Genre:</strong> \{{ book.genre.all|join:", " }}</p>
-
-  <div style="margin-left:20px;margin-top:20px">
-    <h4>Copies</h4>
-
-    {% for copy in book.bookinstance_set.all %}
-      <hr>
-      <p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}">
-        \{{ copy.get_status_display }}
-      </p>
-      {% if copy.status != 'a' %}
-        <p><strong>Due to be returned:</strong> \{{ copy.due_back }}</p>
-      {% endif %}
-      <p><strong>Imprint:</strong> \{{ copy.imprint }}</p>
-      <p class="text-muted"><strong>Id:</strong> \{{ copy.id }}</p>
-    {% endfor %}
-  </div>
-{% endblock %}
- - - -
-

Note : 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 :

- -
<a href="{% url 'author-detail' book.author.pk %}">\{{ book.author }}</a>
-
-
- -

Bien qu'en un peu plus grand, presque tout ce qu'il y a dans ce template a été décrit précédemment :

- - - -

Une chose intéressante que nous n'avons pas encore vue, c'est la fonction book.bookinstance_set.all(). Cette méthode est "automagiquement" construite par Django pour retourner l'ensemble des enregistrements BookInstance associés à un Book particulier.

- -
{% for copy in book.bookinstance_set.all %}
-  <!-- code to iterate across each copy/instance of a book -->
-{% endfor %}
- -

Cette méthode est requise parce que vous déclarez un champ ForeignKey (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 ForeignKey, suivi de _set (ainsi la fonction créée dans Book est bookinstance_set()).

- -
-

Note : Ici nous utilisons all() pour récupérer tous les enregistrements (comportement par défaut). Bien que vous puissiez utiliser la méthode filter() 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.

- -

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 :

- -
[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: <QuerySet [<Author: Ortiz, David>, <Author: H. McRaven, William>, <Author: Leigh, Melinda>]>
-  allow_empty_first_page=allow_empty_first_page, **kwargs)
-
- -

Ceci vient du fait que l'objet paginator 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 !

- -

Ce tutoriel n'a pas (encore !) traité de la pagination, mais comme vous ne pouvez pas utiliser sort_by() et passer un paramètre (pour la même raison que le filter() décrit précédemment), vous avez le choix entre trois options :

- -
    -
  1. Ajouter un ordering lors de la déclaration de la class Meta dans votre modèle.
  2. -
  3. Ajouter un attribut queryset dans votre vue personnalisée basée sur classe, en spécifiant un order_by().
  4. -
  5. Ajouter une méthode get_queryset à votre vue personnalisée basée sur classe, et préciser de même un order_by().
  6. -
- -

Si vous décidez d'ajouter une class Meta au modèle Author (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 :

- -
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}'
-
-    class Meta:
-        ordering = ['last_name']
- -

Bien sûr le champ n'est pas forcément last_name : ce pourrait être un autre champ.

- -

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.

-
- -

À quoi cela ressemble-t-il ?

- -

À 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 (python3 manage.py runserver) et ouvrez votre navigateur à l'adresse http://127.0.0.1:8000/.

- -
-

Attention : Ne cliquez pas sur les liens vers le détail des auteurs : vous allez les créer lors du prochain défi !

-
- -

Cliquez sur le lien Tous les livres pour afficher la liste des livres.

- -

Book List Page

- -

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 :

- -

Book Detail Page

- -

Pagination

- -

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.

- -

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 !

- -

Vues

- -

Ouvrez catalog/views.py, et ajoutez la ligne paginate_by, en gras ci-dessous.

- -
class BookListView(generic.ListView):
-    model = Book
-    paginate_by = 10
- -

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 /catalog/books/?page=2.

- -

Templates

- -

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.

- -

Ouvrez /locallibrary/catalog/templates/base_generic.html, 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).

- -
{% block content %}{% endblock %}
-
-  {% block pagination %}
-    {% if is_paginated %}
-        <div class="pagination">
-            <span class="page-links">
-                {% if page_obj.has_previous %}
-                    <a href="\{{ request.path }}?page=\{{ page_obj.previous_page_number }}">previous</a>
-                {% endif %}
-                <span class="page-current">
-                    Page \{{ page_obj.number }} of \{{ page_obj.paginator.num_pages }}.
-                </span>
-                {% if page_obj.has_next %}
-                    <a href="\{{ request.path }}?page=\{{ page_obj.next_page_number }}">next</a>
-                {% endif %}
-            </span>
-        </div>
-    {% endif %}
-  {% endblock %} 
- -

Le page_obj est un objet Paginator 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.

- -

Nous utilisons \{{ request.path }} 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.

- -

C'est tout !

- -

À quoi cela ressemble-t-il ?

- -

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 paginate_by dans votre  fichier catalog/views.py. Pour obtenir le résultat ci-dessous, nous avons changé la ligne en paginate_by = 2.

- -

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.

- -

Book List Page - paginated

- -

Mettez-vous vous-même au défi !

- -

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 :

- - - -

Le code requis pour le mappeur d'URL et les vues sera virtuellement identique aux vues liste et détail du modèle Book, créées ci-dessus. Les templates seront différents, mais auront un comportement semblable.

- -
-

Note :

- - -
- -

Quand vous aurez fini, vos pages vont ressembler aux captures d'écran suivantes.

- -

Author List Page

- - - -

Author Detail Page

- - - -

Résumé

- -

Félicitations ! Notre application basique pour bibliothèque est maintenant terminée.

- -

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.

- -

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.

- -

Voyez aussi

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}

- -

In this module

- - diff --git a/files/fr/learn/server-side/django/generic_views/index.md b/files/fr/learn/server-side/django/generic_views/index.md new file mode 100644 index 0000000000..8917b2d6fc --- /dev/null +++ b/files/fr/learn/server-side/django/generic_views/index.md @@ -0,0 +1,635 @@ +--- +title: 'Tutoriel Django - 6e partie : Vues génériques pour les listes et les détails' +slug: Learn/Server-side/Django/Generic_views +tags: + - Beginner + - Learn + - Tutorial + - django + - django templates + - django views +translation_of: Learn/Server-side/Django/Generic_views +original_slug: Learn/Server-side/Django/Vues_generiques +--- +
+ +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}
+ +
Ce tutoriel améliore notre site web LocalLibrary, 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.
+ + + + + + + + + + + + +
Prérequis: +

Avoir terminé tous les tutoriels précédents, y compris Django Tutorial Part 5: Creating our home page.

+
Objectif: +

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.

+
+ +

Aperçu

+ +

Dans ce tutoriel, nous allons terminer la première version du site web LocalLibrary, 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 !).

+ +

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.

+ +

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.

+ +

Page de liste de livres

+ +

La page de liste des livres va afficher une liste de tous les enregistrements de livres disponibles, en utilisant l'URL: catalog/books/. 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 (base_generic.html) que nous avons créé dans le tutoriel précédent.

+ +

Mappage d'URL

+ +

Ouvrez le fichier /catalog/urls.py, et copiez-y la ligne en gras ci-dessous. Comme pour la page d'index, cette fonction path() définit un pattern destiné à identifier l'URL ('books/'), une fonction vue qui sera appelée si l'URL correspond (views.BookListView.as_view()), et un nom pour ce mappage particulier.

+ +
urlpatterns = [
+    path('', views.index, name='index'),
+    path('books/', views.BookListView.as_view(), name='books'),
+]
+ +

Comme discuté dans le tutoriel précédent, l'URL doit auparavant avoir identifié la chaîne /catalog, aussi la vue ne sera réellement appelée que pour l'URL complète: /catalog/books/.

+ +

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.

+ +

En Django, on accède à la fonction appropriée d'une vue basée sur classe en appelant sa méthode de classe as_view(). 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.

+ +

Vue (basée sur classe)

+ +

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 render() 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 (ListView), 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.

+ +

Ouvrez le fichier catalog/views.py, et copiez-y le code suivant à la fin :

+ +
from django.views import generic
+
+class BookListView(generic.ListView):
+    model = Book
+ +

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é (Book), et ensuite rendre un template situé à l'adresse /locallibrary/catalog/templates/catalog/book_list.html (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 object_list OU book_list (c'est-à-dire l'appellation générique "the_model_name_list").

+ +
+

Note : Ce chemin étrange vers le lieu du template n'est pas une faute de frappe : les vues génériques cherchent les templates dans /application_name/the_model_name_list.html (catalog/book_list.html dans ce cas) à l'intérieur du répertoire /application_name/templates/ (/catalog/templates/).

+
+ +

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.

+ +
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
+ +

Ré-écrire des méthodes dans des vues basées sur classes

+ +

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.

+ +

Par exemple, nous pouvons ré-écrire la méthode get_queryset() pour changer la liste des enregistrements retournés. Cette façon de faire est plus flexible que simplement définir l'attribut queryset, 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) :

+ +
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
+
+ +

Nous pourrions aussi réécrire get_context_data(), 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 "some_data" au contexte (elle sera alors accessible comme variable de template).

+ +
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
+ +

Quand vous faites cela, il est important de suivre la procédure indiquée ci-dessus :

+ + + +
+

Note : Voyez dans Built-in class-based generic views (doc de Django) pour avoir beaucoup plus d'exemples de ce que vous pouvez faire.

+
+ +

Créer le template pour la Vue Liste

+ +

Créez le fichier HTML /locallibrary/catalog/templates/catalog/book_list.html, 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é Book, dans une application appelée catalog).

+ +

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 index, nous étendons notre template de base à la première ligne, et remplaçons ensuite le bloc appelé content.

+ +
{% extends "base_generic.html" %}
+
+{% block content %}
+  <h1>Book List</h1>
+  {% if book_list %}
+  <ul>
+    {% for book in book_list %}
+      <li>
+        <a href="\{{ book.get_absolute_url }}">\{{ book.title }}</a> (\{{book.author}})
+      </li>
+    {% endfor %}
+  </ul>
+  {% else %}
+    <p>There are no books in the library.</p>
+  {% endif %} 
+{% endblock %}
+ +

La vue envoie le contexte (liste de livres), en utilisant par défaut les alias object_list et book_list ; l'un et l'autre fonctionnent.

+ +

Exécution conditionnelle

+ +

Nous utilisons les balises de templates if, else, et endif pour vérifier que la book_list a été définie et n'est pas vide. Si book_list est vide, alors la condition else affiche un texte expliquant qu'il n'y a pas de livres à lister. Si book_list n'est pas vide, nous parcourons la liste de livres.

+ +
{% if book_list %}
+  <!-- code here to list the books -->
+{% else %}
+  <p>There are no books in the library.</p>
+{% endif %}
+
+ +

La condition ci-dessus ne vérifie qu'un seul cas, mais vous pouvez ajouter d'autres tests grâce à la balise de template elif (par exemple {% elif var2 %}). Pour plus d'information sur les opérateurs conditionnels, voyez ici :  if, ifequal/ifnotequal, et ifchanged dans Built-in template tags and filters (Django Docs).

+ +

Boucles for

+ +

Le template utilise les balises de template for et endfor pour boucler à travers la liste de livres, comme montré ci-dessous. Chaque itération peuple la variable de template book avec l'information concernant l'élément courant de la liste.

+ +
{% for book in book_list %}
+  <li> <!-- code here get information from each book item --> </li>
+{% endfor %}
+
+ +

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 forloop.last pour réaliser une action conditionnelle au dernier passage de la boucle.

+ +

Accéder aux variables

+ +

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.

+ +
<a href="\{{ book.get_absolute_url }}">\{{ book.title }}</a> (\{{book.author}})
+
+ +

Nous accédont aux champs de l'enregistrement "livre" associé, en utilisant la notation "à points" (par exemple book.title et book.author), où le texte suivant l'item book est le nom du champ (comme défini dans le modèle).

+ +

Nous pouvons aussi appeler des fonctions contenues dans le modèle depuis l'intérieur de notre template — dans ce cas nous appelons Book.get_absolute_url() 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 !).

+ +
+

Note : 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 !

+
+ +

Mettre à jour le template de base

+ +

Ouvrez le template de base (/locallibrary/catalog/templates/base_generic.html) et insérez {% url 'books' %} dans le lien URL pour All books, 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").

+ +
<li><a href="{% url 'index' %}">Home</a></li>
+<li><a href="{% url 'books' %}">All books</a></li>
+<li><a href="">All authors</a></li>
+ +

À quoi cela ressemble-t-il ?

+ +

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.

+ +

Page de détail d'un livre

+ +

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 catalog/book/<id> (où <id> est la clé primaire pour le livre). En plus des champs définis dans le modèle Book (auteur, résumé, ISBN, langue et genre), nous allons aussi lister les détails des copies disponibles (BookInstances), 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.

+ +

Mappage d'URL

+ +

Ouvrez /catalog/urls.py et ajoutez-y le mappeur d'URL 'book-detail' indiqué en gras ci-dessous. Cette fonction path() définit un pattern, la vue générique basée sur classe qui lui est associée, ainsi qu'un nom.

+ +
urlpatterns = [
+    path('', views.index, name='index'),
+    path('books/', views.BookListView.as_view(), name='books'),
+    path('book/<int:pk>', views.BookDetailView.as_view(), name='book-detail'),
+]
+ +

Pour le chemin book-detail, 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 ('<' et '>') 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, <something>  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 spécification de convertisseur, qui définit le type de la donnée (int, str, slug, uuid, path).

+ +

Dans ce cas, nous utilisons '<int:pk>' 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é pk (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.

+ +
+

Note : Comme nous l'avons dit précédemment, notre URL correcte est en réalité catalog/book/<digits> (comme nous sommes dans l'application catalog/catalog/ est supposé).

+
+ +
+

Attention : La vue générique basée sur classe "détail" s'attend à recevoir un paramètre appelé pk. 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é.

+
+ +

Introduction aux chemins et expressions régulières avancés

+ +
+

Note : 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.

+
+ +

La recherche de pattern fournie par path() est simple et utile pour les cas (très communs) où vous voulez seulement capturer n'importe quelle 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 re_path().

+ +

Cette méthode est utilisée exactement comme path(), sauf qu'elle vous permet de spécifier un pattern utilisant une Expression régulière. Par exemple, le chemin précédent pourrait avoir été écrit ainsi :

+ +
re_path(r'^book/(?P<pk>\d+)$', views.BookDetailView.as_view(), name='book-detail'),
+
+ +

Les expressions régulières 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 !

+ +

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 : r'<votre texte d'expression régulière va ici>').

+ +

L'essentiel de ce que vous aurez besoin de savoir pour déclarer une recherche de pattern est contenu dans le tableau qui suit :

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SymbolMeaning
^Recherche le début du texte.
$Recherche la fin du texte.
\dRecherche un digit (0, 1, 2, ... 9).
\wRecherche un caractère de mot, c'est-à-dire tout caractère dans l'alphabet (majuscule ou minuscule), un digit ou un underscore (_).
+Recherche au moins une occurence du caractère précédent. Par exemple, pour rechercher au moins 1 digit, vous utiliseriez \d+. Pour rechercher au moins 1 caractère "a", vous utiliseriez a+.
*Recherche zéro ou plus occurrence(s) du caractère précédent. Par exemple, pour rechercher "rien ou un mot", vous pourriez utiliser \w*.
( )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).
(?P<name>...)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 !
[  ]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.
+ +

La plupart des autres caractères peuvent être pris littéralement.

+ +

Considérons quelques exemples réels de patterns :

+ + + + + + + + + + + + + + + + + + + + + + +
PatternDescription
r'^book/(?P<pk>\d+)$' +

C'est là l'expression régulière utilisée dans notre mappeur d'URL. Elle recherche une chaîne qui a book/ au commencement de la ligne (^book/), ensuite a au moins 1 digit (\d+), et enfin se termine (avec aucun caractère non-digit avant la fin du marqueur de ligne).

+ +

Elle capture aussi tous les digits (?P<pk>\d+) et les passe à la vue dans un paramètre appelé 'pk'. Les valeurs capturées sont toujours passées comme des chaînes !

+ +

Par exemple, cette expression régulière trouverait une correspondance dans l'URL book/1234, et enverrait alors une variable pk='1234' à la vue.

+
r'^book/(\d+)$' +

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é.

+
r'^book/(?P<stub>[-\w]+)$' +

Ceci recherche une chaîne qui a book/ au commencement de la ligne (^book/), ensuite a au moins 1 caractère étant soit un '-', soit un caractère de mot ([-\w]+), puis la fin. Ce pattern capture aussi cet ensemble de caractères et le passe à la vue en tant que paramètre nommé 'stub'.

+ +

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 /catalog/book/the-secret-garden, plutôt que /catalog/book/33.

+
+ +

Vous pouvez capturer plusieurs patterns en une seule fois, et donc encoder beaucoup d'informations différentes dans l'URL.

+ +
+

Note : 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.

+
+ +

Passer des options supplémentaires dans vos mappages d'URL

+ +

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 options supplémentaires. Les options sont déclarées comme un dictionnaire que vous passez comme troisième argument (non nommé) à la fonction path(). 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).

+ +
path('url/', views.my_reused_view, {'my_template_name': 'some_path'}, name='aurl'),
+path('anotherurl/', views.my_reused_view, {'my_template_name': 'another_path'}, name='anotherurl'),
+
+ +
+

Note : Les options supplémentaires aussi bien que les patterns capturés sont passés à la vue comme arguments nommés. Si vous utilisez le même nom 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).

+
+ +

Vue (basée sur classe)

+ +

Ouvrez catalog/views.py, et copiez-y le code suivant à la fin du fichier :

+ +
class BookDetailView(generic.DetailView):
+    model = Book
+ +

C'est tout ! La seule chose que vous avez à faire maintenant, c'est créer un template appelé /locallibrary/catalog/templates/catalog/book_detail.html, et la vue va lui passer les informations de la base de donnée concernant l'enregistrement Book 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 object OU book (c'est-à-dire, de manière générique, "le_nom_du_modèle").

+ +

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.

+ +

Que se passe-t-il si l'enregistrement n'existe pas ?

+ +

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 Http404 — en production, cela va automatiquement afficher une page appropriée "ressource non trouvée", que vous pouvez personnaliser si besoin.

+ +

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 pas la vue générique basée sur classe "détail".

+ +
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})
+
+ +

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 Http404 pour indiquer que le livre est "non trouvé". L'étape finale est ensuite, comme d'habitude, d'appeler render() avec le nom du template et les données concernant le livre dans le paramètre context (comme un dictionnaire).

+ +

Une alternative consiste à utiliser la fonction get_object_or_404() comme un raccourci pour lever une exception Http404 si l'enregistrement n'existe pas.

+ +
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})
+ +

Créerle template de la Vue Détail

+ +

Créez le fichier HTML /locallibrary/catalog/templates/catalog/book_detail.html, 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 detail (pour un modèle appelé Book dans une application appelée catalog).

+ +
{% extends "base_generic.html" %}
+
+{% block content %}
+  <h1>Title: \{{ book.title }}</h1>
+
+  <p><strong>Author:</strong> <a href="">\{{ book.author }}</a></p> <!-- author detail link not yet defined -->
+  <p><strong>Summary:</strong> \{{ book.summary }}</p>
+  <p><strong>ISBN:</strong> \{{ book.isbn }}</p>
+  <p><strong>Language:</strong> \{{ book.language }}</p>
+  <p><strong>Genre:</strong> \{{ book.genre.all|join:", " }}</p>
+
+  <div style="margin-left:20px;margin-top:20px">
+    <h4>Copies</h4>
+
+    {% for copy in book.bookinstance_set.all %}
+      <hr>
+      <p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}">
+        \{{ copy.get_status_display }}
+      </p>
+      {% if copy.status != 'a' %}
+        <p><strong>Due to be returned:</strong> \{{ copy.due_back }}</p>
+      {% endif %}
+      <p><strong>Imprint:</strong> \{{ copy.imprint }}</p>
+      <p class="text-muted"><strong>Id:</strong> \{{ copy.id }}</p>
+    {% endfor %}
+  </div>
+{% endblock %}
+ + + +
+

Note : 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 :

+ +
<a href="{% url 'author-detail' book.author.pk %}">\{{ book.author }}</a>
+
+
+ +

Bien qu'en un peu plus grand, presque tout ce qu'il y a dans ce template a été décrit précédemment :

+ + + +

Une chose intéressante que nous n'avons pas encore vue, c'est la fonction book.bookinstance_set.all(). Cette méthode est "automagiquement" construite par Django pour retourner l'ensemble des enregistrements BookInstance associés à un Book particulier.

+ +
{% for copy in book.bookinstance_set.all %}
+  <!-- code to iterate across each copy/instance of a book -->
+{% endfor %}
+ +

Cette méthode est requise parce que vous déclarez un champ ForeignKey (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 ForeignKey, suivi de _set (ainsi la fonction créée dans Book est bookinstance_set()).

+ +
+

Note : Ici nous utilisons all() pour récupérer tous les enregistrements (comportement par défaut). Bien que vous puissiez utiliser la méthode filter() 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.

+ +

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 :

+ +
[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: <QuerySet [<Author: Ortiz, David>, <Author: H. McRaven, William>, <Author: Leigh, Melinda>]>
+  allow_empty_first_page=allow_empty_first_page, **kwargs)
+
+ +

Ceci vient du fait que l'objet paginator 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 !

+ +

Ce tutoriel n'a pas (encore !) traité de la pagination, mais comme vous ne pouvez pas utiliser sort_by() et passer un paramètre (pour la même raison que le filter() décrit précédemment), vous avez le choix entre trois options :

+ +
    +
  1. Ajouter un ordering lors de la déclaration de la class Meta dans votre modèle.
  2. +
  3. Ajouter un attribut queryset dans votre vue personnalisée basée sur classe, en spécifiant un order_by().
  4. +
  5. Ajouter une méthode get_queryset à votre vue personnalisée basée sur classe, et préciser de même un order_by().
  6. +
+ +

Si vous décidez d'ajouter une class Meta au modèle Author (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 :

+ +
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}'
+
+    class Meta:
+        ordering = ['last_name']
+ +

Bien sûr le champ n'est pas forcément last_name : ce pourrait être un autre champ.

+ +

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.

+
+ +

À quoi cela ressemble-t-il ?

+ +

À 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 (python3 manage.py runserver) et ouvrez votre navigateur à l'adresse http://127.0.0.1:8000/.

+ +
+

Attention : Ne cliquez pas sur les liens vers le détail des auteurs : vous allez les créer lors du prochain défi !

+
+ +

Cliquez sur le lien Tous les livres pour afficher la liste des livres.

+ +

Book List Page

+ +

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 :

+ +

Book Detail Page

+ +

Pagination

+ +

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.

+ +

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 !

+ +

Vues

+ +

Ouvrez catalog/views.py, et ajoutez la ligne paginate_by, en gras ci-dessous.

+ +
class BookListView(generic.ListView):
+    model = Book
+    paginate_by = 10
+ +

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 /catalog/books/?page=2.

+ +

Templates

+ +

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.

+ +

Ouvrez /locallibrary/catalog/templates/base_generic.html, 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).

+ +
{% block content %}{% endblock %}
+
+  {% block pagination %}
+    {% if is_paginated %}
+        <div class="pagination">
+            <span class="page-links">
+                {% if page_obj.has_previous %}
+                    <a href="\{{ request.path }}?page=\{{ page_obj.previous_page_number }}">previous</a>
+                {% endif %}
+                <span class="page-current">
+                    Page \{{ page_obj.number }} of \{{ page_obj.paginator.num_pages }}.
+                </span>
+                {% if page_obj.has_next %}
+                    <a href="\{{ request.path }}?page=\{{ page_obj.next_page_number }}">next</a>
+                {% endif %}
+            </span>
+        </div>
+    {% endif %}
+  {% endblock %} 
+ +

Le page_obj est un objet Paginator 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.

+ +

Nous utilisons \{{ request.path }} 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.

+ +

C'est tout !

+ +

À quoi cela ressemble-t-il ?

+ +

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 paginate_by dans votre  fichier catalog/views.py. Pour obtenir le résultat ci-dessous, nous avons changé la ligne en paginate_by = 2.

+ +

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.

+ +

Book List Page - paginated

+ +

Mettez-vous vous-même au défi !

+ +

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 :

+ + + +

Le code requis pour le mappeur d'URL et les vues sera virtuellement identique aux vues liste et détail du modèle Book, créées ci-dessus. Les templates seront différents, mais auront un comportement semblable.

+ +
+

Note :

+ + +
+ +

Quand vous aurez fini, vos pages vont ressembler aux captures d'écran suivantes.

+ +

Author List Page

+ + + +

Author Detail Page

+ + + +

Résumé

+ +

Félicitations ! Notre application basique pour bibliothèque est maintenant terminée.

+ +

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.

+ +

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.

+ +

Voyez aussi

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}

+ +

In this module

+ + diff --git a/files/fr/learn/server-side/django/home_page/index.html b/files/fr/learn/server-side/django/home_page/index.html deleted file mode 100644 index 5c8f63c457..0000000000 --- a/files/fr/learn/server-side/django/home_page/index.html +++ /dev/null @@ -1,429 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}
- -

Le travail préparatoire pour nous permettre de créer une page d'accueil pour le site web de la bibliothèque locale 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.

- - - - - - - - - - - - -
Pré-requis:L'introduction à cette série didactique et les sections précédentes y compris celle sur le site d'administration du site web.
Objective: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.
- -

Survol

- -

Dans les sections précédentes, nous avons défini le modèle de données et les objets Dajngo à manipuler, puis nous avons commencé à peupler des enregistrements à l'aide du site d'administration. 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.

- -

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 à :

- - - -

- -

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.

- -

Identifier les URLs des ressources

- -

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.

- -

La liste des URLs dont nous aurons besoin se résume à :

- - - -

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.

- -

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).

- -
-

Note : 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 GET avec un passage de paramètres (/book/?id=6). 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  recommandé par le W3C.

- -

La documentation de Django recommande aussi de coder les informations dans le corps des URLs pour avoir une meilleure conception de ces URLs.

-
- -

La suite de cette section s'intéresse à la conception de la page d'accueil.

- -

Création de la page d'accueil

- -

La toute première page à créer est la page d'accueil (catalog/). 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. 

- -
-

Note : 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.

-
- -

Routage d'URL

- -

Quand nous avons créé le squelette du site, nous avons mis à jour les routage des urls dans le fichier locallibrary/urls.py afin de nous assurer que toutes les requêtes démarrant par catalog/  seront traités par le configurateur URLConf du module catalog.urls qui traitera la sous-chaîne restante.

- -

L'extrait du code ci-dessous permet d'intégrer dans locallibrary/urls.py le configurateur d'url du module catalog :

- -
urlpatterns += [
-    path('catalog/', include('catalog.urls')),
-]
-
- -

Il est désormais nécessaire de créer un configurateur d'URL du module catalog (URLConf du module est nommé /catalog/urls.py). Ajoutez le chemin ci-dessous :

- -
urlpatterns = [
-    path('', views.index, name='index'),
-]
- -

La fonction path() sert à définir les éléments suivants :

- - - -

La paramètre name utilisé dans la fonction path() 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 :

- -
<a href="{% url 'index' %}">Home</a>.
- -
-

Note : Vus pourriez bien évidemment coder en dur l'accès à la page d'accueil de cette manaière <a href="/catalog/">Home</a>), mais si nous changions le modèle d'URL, par exemple en /catalog/index, 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.

-
- -

Vue (View function-based)

- -

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 index() 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.

- -

Editer le fichier catalog/views.py.Vous constaterez l'import de la fonction render() qui traite de la génération HTML en utilisat des garabits et des données : 

- -
from django.shortcuts import render
-
-# Create your views here.
-
- -

Copiez les lignes ci-dessous dans le fichier :

- -
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)
- -

La première ligne de code permet d'importer les modèles de données du catalogue décrites dans le module catalog.

- -

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 models objects.all() sur les objets Book et BookInstance. En sus, nous recherchons les ouvrages disponibles, ce qui revient à faire une requête avec un filtre sur l'attribut status de l'objet BookInstance ayant la valeur 'a' (Available). Si vous avez un oubli, vous pouvez consulter La section 3 de Django didactique : utilisation du modèle de données > Chercher des enregistrements.

- -

La dernière ligne de cette fonction est l'appel de la fonction render() 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 render() utilise les paramètres :

- - - -

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.

- -

Gabarit (Template)

- -

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.

- -

La cadriciel Django va rechercher automatiquement ces gabarits dans un répertoire nommé 'templates' dans le dossier de l'application. Si vous reprenez la dernière ligne de la de fonctions index() dans l'exemple ci-dessus, la fonction render() a besoin du fichier index.html qui devrait être placé dans le dossier /locallibrary/catalog/templates/. Dans le cas contraire, cela génère une erreur car le fichier est considéré comme absent.

- -

Vous pouvez en faire l'expérience dès à présent, après avoir redémarré votre serveur local, en accédant à l'URL 127.0.0.1:8000 de votre navigateur. Une page d'erreur explicite s'affiche en indiquant un message du type : "TemplateDoesNotExist at /catalog/", ainsi que de nombreux détails sur l'enchaînement des fonctions aboutissant à cette erreur.

- -
-

Note : 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 documentation Django relative aux gabarit.

-
- -

Concevoir les gabarits

- -

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.

- -

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 base_generic.html, vous constaterez qu'il s'y mélange du code HTML et des sections nommées contenu dans entre des marqueurs block et endblock qui peut contenir ou non des données.

- -
-

Note : 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. 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).

-
- -

Dans l'extrait ci-dessous vous avec trois sections nommées qui pourront être remplacés par la suite :

- - - -
<!DOCTYPE html>
-<html lang="fr">
-<head>
-  {% block title %}<title>Bibliothèque locale</title>{% endblock %}
-</head>
-<body>
-  {% block sidebar %}<!-- insert default navigation text for every page -->{% endblock %}
-  {% block content %}<!-- default content text (typically empty) -->{% endblock %}
-</body>
-</html>
-
- -

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 extends 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 block/endblock qui définissent le début et la fin de section.

- -

A titre indicatif, l'extrait ci-dessous présente la manière d'activer à l'aide de la balise extends le remplacement de la section content. 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 title, mais avec les éléments nouveaux, ci-dessous, pour la section content.

- -
{% extends "base_generic.html" %}
-
-{% block content %}
-  <h1>Accueil de la bibliothèque locale</h1>
-  <p>Bienvenue sur la bibliothèque locale, un site web développé par <em>Mozilla Developer Network</em>!</p>
-{% endblock %}
- -

Le gabarit de base de la bibliothèque

- -

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 title, sidebar, et content. La section title contient un titre par défaut. De même la section sidebar contient un liens vers la liste des livres et des auteurs qui pourra être modifié ensuite.

- -
-

Note : Il y a aussi deux balises supplémentaires : url et load static. Elles seront étudiées dans le chapitre suivant.

-
- -

Créez un nouveau fichier nommé base_generic.html dans le dossier /locallibrary/catalog/templates/ (à créer aussi) et copiez-y le texte ci-dessous :

- -
<!DOCTYPE html>
-<html lang="en">
-<head>
-  {% block title %}<title>Bibliothèque locale</title>{% endblock %}
-  <meta charset="utf-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1">
-  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
-  <!-- Add additional CSS in static file -->
-  {% load static %}
-  <link rel="stylesheet" href="{% static 'css/styles.css' %}">
-</head>
-<body>
-  <div class="container-fluid">
-    <div class="row">
-      <div class="col-sm-2">
-          {% block sidebar %}
-           <ul class="sidebar-nav">
-               <li><a href="{% url 'index' %}">Home</a></li>
-               <li><a href="">Tous les livres</a></li>
-               <li><a href="">Tous les auteurs</a></li>
-           </ul>
-          {% endblock %}
-      </div>
-         <div class="col-sm-10 ">
-             {% block content %}
-             {% endblock %}
-         </div>
-    </div>
-  </div>
-</body>
-</html>
- -

Ce gabarit fait appel - en ligne 7 - à la bibliothèque de style Bootstrap 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.

- -

De même, ce gabarit fait appel à une feuille de style - en ligne 10 - locale pour ajouter ou adapter des styles. Créez le fichier styles.css dans le répertoire /locallibrary/catalog/static/css/ (à créer aussi) et copiez le contenu ci-dessous :

- -
.sidebar-nav {
-    margin-top: 20px;
-    padding: 0;
-    list-style: none;
-}
- -

La page d'accueil

- -

Maintenant créez le fichier HTML index.html dans le dossier /locallibrary/catalog/templates/ et copiez-y le code ci-dessous. This code extends our base template on the first line, and then replaces the default content block for the template. 

- -
{% extends "base_generic.html" %}
-
-{% block content %}
-  <h1>Accueil de la bibliothèque locale</h1>
-  <p>Bienvenue à la bibliothèque locale, un site web développé par <em>Mozilla Developer Network</em>!</p>
-  <h2>Contenu dynamique</h2>
-  <p>La bibliothèque dispose des enregistrements suivants:</p>
-  <ul>
-    <li><strong>Livres:</strong> \{{ num_books }}</li>
-    <li><strong>Copies:</strong> \{{ num_instances }}</li>
-    <li><strong>Copies disponibles:</strong> \{{ num_instances_available }}</li>
-    <li><strong>Auteurs:</strong> \{{ num_authors }}</li>
-  </ul>
-{% endblock %}
- -

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. 

- -
-

Note: Vous pouvez constatez simplement que les balises de gabarit (fonctions) et les baslises de variables sont entre accolades ; double accolades pour uen variable (\{{ num_books }}), et simple accolade avec le pourcentage ({% extends "base_generic.html" %}) pour les balises.

-
- -

Gardez en mémoire que les variables utilisées dans les gabarits sont des clés d'un dictionnaires context transmis à la fonction render() de la vue (revenez à l'exemple plus haut, ou l'extrait ci-dessous). La fonction render() traitera le dictionnaire pour restituer uen page HTML où les variables nommées auront été remplacées par leur valeur dans le dictionnaire.

- -
context = {
-    'num_books': num_books,
-    'num_instances': num_instances,
-    'num_instances_available': num_instances_available,
-    'num_authors': num_authors,
-}
-
-return render(request, 'index.html', context=context)
- -

Traiter les fichiers statiques dans les gabarits

- -

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 STATIC_URL. Le paramétrage par défaut du site web définit la variable STATIC_URL à '/static/', mais vous pouvez choisir d'heberger ailleurs.

- -

Au sein du gabarit, vous ferrez appel à la balise load en précisant "static" pour faire votre ajout, comme décrits dans l'extrait ci-dessous. Vous utilisez la balise static et vous spécifiez ensuite l'URL pour accéder au fichier nécessaire.

- -
<!-- Add additional CSS in static file -->
-{% load static %}
-<link rel="stylesheet" href="{% static 'css/styles.css' %}">
- -

De la même manière, vous pouvez par exemple :

- -
{% load static %}
-<img src="{% static 'catalog/images/local_library_model_uml.png' %}" alt="UML diagram" style="width:555px;height:540px;">
-
- -
-

Note : 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 (/locallibrary/locallibrary/urls.py) à la création du squelette du site. Cependant nous devrons travailler plus tard la mise en production.

-
- -

Pour plus de détails sur les fichiers statiques vous pouvez consulter la documentation Django sur la gestion des fichiers statiques.

- -

Associer des URL

- -

L'exemple ci-dessous introduit l'utilisation de la balise de gabarit url.

- -
<li><a href="{% url 'index' %}">Home</a></li>
-
- -

Cette balise accepte des référence enregistrées par la fonction path() appelée dans les fichiers  urls.py 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.

- -

Où trouver les gabarits...

- -

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 settings.py comme indiqué en gras ci-dessous :

- -
TEMPLATES = [
-    {
-        'BACKEND': 'django.template.backends.django.DjangoTemplates',
-        'DIRS': [
-            os.path.join(BASE_DIR, 'templates'),
-        ],
-        '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',
-            ],
-        },
-    },
-]
- -

A quoi cela devrait-il ressembler ?

- -

A ce niveau, nous avons créé l'ensemble des ressources nécessaires à l'affichage de la page d'accueil. Démarrez le serveur (python3 manage.py runserver) et accédez avec votre navigateur à la page d'accueil du site web http://127.0.0.1:8000/. Si tout va bien, vous devriez avoir une page qui ressemble à celle ci-dessous.

- -

Page d'accueil en Français

- -
-

Note: Toutes les ressources n'ayant pas été encore créées les liens vers Tous les livres et Tous les auteurs ne fonctionnent pas encore.

-
- -

Défi

- -

Voici deux suggestion pour tester votre connaissance de Django et des requêtes, vues et gabarits :

- -
    -
  1. La bibliothèque locale dispose d'un gabarit d'origine qui inclu une section title. Surchargez cette section dans le gabarit index et créer un nouveau titre. - -
    -

    Note : La section Concevoir un gabarit détaille la manière de modifier une section.

    -
    -
  2. -
  3. 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  context. Pour faire cela utilisez les variables num_books et num_instances_available. Ensuite vous pourrez mettre à jour le gabarit de la page d'accueil.
    -  
  4. -
- - - -

Résumé

- -

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.

- -

Nous allons nous appuyer sur ces éléments pour concevoir dans le prochain chapitres les 4 pages qui manquent.

- -

À voir aussi

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/server-side/django/home_page/index.md b/files/fr/learn/server-side/django/home_page/index.md new file mode 100644 index 0000000000..5c8f63c457 --- /dev/null +++ b/files/fr/learn/server-side/django/home_page/index.md @@ -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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}
+ +

Le travail préparatoire pour nous permettre de créer une page d'accueil pour le site web de la bibliothèque locale 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.

+ + + + + + + + + + + + +
Pré-requis:L'introduction à cette série didactique et les sections précédentes y compris celle sur le site d'administration du site web.
Objective: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.
+ +

Survol

+ +

Dans les sections précédentes, nous avons défini le modèle de données et les objets Dajngo à manipuler, puis nous avons commencé à peupler des enregistrements à l'aide du site d'administration. 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.

+ +

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 à :

+ + + +

+ +

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.

+ +

Identifier les URLs des ressources

+ +

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.

+ +

La liste des URLs dont nous aurons besoin se résume à :

+ + + +

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.

+ +

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).

+ +
+

Note : 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 GET avec un passage de paramètres (/book/?id=6). 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  recommandé par le W3C.

+ +

La documentation de Django recommande aussi de coder les informations dans le corps des URLs pour avoir une meilleure conception de ces URLs.

+
+ +

La suite de cette section s'intéresse à la conception de la page d'accueil.

+ +

Création de la page d'accueil

+ +

La toute première page à créer est la page d'accueil (catalog/). 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. 

+ +
+

Note : 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.

+
+ +

Routage d'URL

+ +

Quand nous avons créé le squelette du site, nous avons mis à jour les routage des urls dans le fichier locallibrary/urls.py afin de nous assurer que toutes les requêtes démarrant par catalog/  seront traités par le configurateur URLConf du module catalog.urls qui traitera la sous-chaîne restante.

+ +

L'extrait du code ci-dessous permet d'intégrer dans locallibrary/urls.py le configurateur d'url du module catalog :

+ +
urlpatterns += [
+    path('catalog/', include('catalog.urls')),
+]
+
+ +

Il est désormais nécessaire de créer un configurateur d'URL du module catalog (URLConf du module est nommé /catalog/urls.py). Ajoutez le chemin ci-dessous :

+ +
urlpatterns = [
+    path('', views.index, name='index'),
+]
+ +

La fonction path() sert à définir les éléments suivants :

+ + + +

La paramètre name utilisé dans la fonction path() 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 :

+ +
<a href="{% url 'index' %}">Home</a>.
+ +
+

Note : Vus pourriez bien évidemment coder en dur l'accès à la page d'accueil de cette manaière <a href="/catalog/">Home</a>), mais si nous changions le modèle d'URL, par exemple en /catalog/index, 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.

+
+ +

Vue (View function-based)

+ +

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 index() 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.

+ +

Editer le fichier catalog/views.py.Vous constaterez l'import de la fonction render() qui traite de la génération HTML en utilisat des garabits et des données : 

+ +
from django.shortcuts import render
+
+# Create your views here.
+
+ +

Copiez les lignes ci-dessous dans le fichier :

+ +
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)
+ +

La première ligne de code permet d'importer les modèles de données du catalogue décrites dans le module catalog.

+ +

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 models objects.all() sur les objets Book et BookInstance. En sus, nous recherchons les ouvrages disponibles, ce qui revient à faire une requête avec un filtre sur l'attribut status de l'objet BookInstance ayant la valeur 'a' (Available). Si vous avez un oubli, vous pouvez consulter La section 3 de Django didactique : utilisation du modèle de données > Chercher des enregistrements.

+ +

La dernière ligne de cette fonction est l'appel de la fonction render() 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 render() utilise les paramètres :

+ + + +

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.

+ +

Gabarit (Template)

+ +

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.

+ +

La cadriciel Django va rechercher automatiquement ces gabarits dans un répertoire nommé 'templates' dans le dossier de l'application. Si vous reprenez la dernière ligne de la de fonctions index() dans l'exemple ci-dessus, la fonction render() a besoin du fichier index.html qui devrait être placé dans le dossier /locallibrary/catalog/templates/. Dans le cas contraire, cela génère une erreur car le fichier est considéré comme absent.

+ +

Vous pouvez en faire l'expérience dès à présent, après avoir redémarré votre serveur local, en accédant à l'URL 127.0.0.1:8000 de votre navigateur. Une page d'erreur explicite s'affiche en indiquant un message du type : "TemplateDoesNotExist at /catalog/", ainsi que de nombreux détails sur l'enchaînement des fonctions aboutissant à cette erreur.

+ +
+

Note : 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 documentation Django relative aux gabarit.

+
+ +

Concevoir les gabarits

+ +

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.

+ +

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 base_generic.html, vous constaterez qu'il s'y mélange du code HTML et des sections nommées contenu dans entre des marqueurs block et endblock qui peut contenir ou non des données.

+ +
+

Note : 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. 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).

+
+ +

Dans l'extrait ci-dessous vous avec trois sections nommées qui pourront être remplacés par la suite :

+ + + +
<!DOCTYPE html>
+<html lang="fr">
+<head>
+  {% block title %}<title>Bibliothèque locale</title>{% endblock %}
+</head>
+<body>
+  {% block sidebar %}<!-- insert default navigation text for every page -->{% endblock %}
+  {% block content %}<!-- default content text (typically empty) -->{% endblock %}
+</body>
+</html>
+
+ +

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 extends 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 block/endblock qui définissent le début et la fin de section.

+ +

A titre indicatif, l'extrait ci-dessous présente la manière d'activer à l'aide de la balise extends le remplacement de la section content. 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 title, mais avec les éléments nouveaux, ci-dessous, pour la section content.

+ +
{% extends "base_generic.html" %}
+
+{% block content %}
+  <h1>Accueil de la bibliothèque locale</h1>
+  <p>Bienvenue sur la bibliothèque locale, un site web développé par <em>Mozilla Developer Network</em>!</p>
+{% endblock %}
+ +

Le gabarit de base de la bibliothèque

+ +

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 title, sidebar, et content. La section title contient un titre par défaut. De même la section sidebar contient un liens vers la liste des livres et des auteurs qui pourra être modifié ensuite.

+ +
+

Note : Il y a aussi deux balises supplémentaires : url et load static. Elles seront étudiées dans le chapitre suivant.

+
+ +

Créez un nouveau fichier nommé base_generic.html dans le dossier /locallibrary/catalog/templates/ (à créer aussi) et copiez-y le texte ci-dessous :

+ +
<!DOCTYPE html>
+<html lang="en">
+<head>
+  {% block title %}<title>Bibliothèque locale</title>{% endblock %}
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
+  <!-- Add additional CSS in static file -->
+  {% load static %}
+  <link rel="stylesheet" href="{% static 'css/styles.css' %}">
+</head>
+<body>
+  <div class="container-fluid">
+    <div class="row">
+      <div class="col-sm-2">
+          {% block sidebar %}
+           <ul class="sidebar-nav">
+               <li><a href="{% url 'index' %}">Home</a></li>
+               <li><a href="">Tous les livres</a></li>
+               <li><a href="">Tous les auteurs</a></li>
+           </ul>
+          {% endblock %}
+      </div>
+         <div class="col-sm-10 ">
+             {% block content %}
+             {% endblock %}
+         </div>
+    </div>
+  </div>
+</body>
+</html>
+ +

Ce gabarit fait appel - en ligne 7 - à la bibliothèque de style Bootstrap 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.

+ +

De même, ce gabarit fait appel à une feuille de style - en ligne 10 - locale pour ajouter ou adapter des styles. Créez le fichier styles.css dans le répertoire /locallibrary/catalog/static/css/ (à créer aussi) et copiez le contenu ci-dessous :

+ +
.sidebar-nav {
+    margin-top: 20px;
+    padding: 0;
+    list-style: none;
+}
+ +

La page d'accueil

+ +

Maintenant créez le fichier HTML index.html dans le dossier /locallibrary/catalog/templates/ et copiez-y le code ci-dessous. This code extends our base template on the first line, and then replaces the default content block for the template. 

+ +
{% extends "base_generic.html" %}
+
+{% block content %}
+  <h1>Accueil de la bibliothèque locale</h1>
+  <p>Bienvenue à la bibliothèque locale, un site web développé par <em>Mozilla Developer Network</em>!</p>
+  <h2>Contenu dynamique</h2>
+  <p>La bibliothèque dispose des enregistrements suivants:</p>
+  <ul>
+    <li><strong>Livres:</strong> \{{ num_books }}</li>
+    <li><strong>Copies:</strong> \{{ num_instances }}</li>
+    <li><strong>Copies disponibles:</strong> \{{ num_instances_available }}</li>
+    <li><strong>Auteurs:</strong> \{{ num_authors }}</li>
+  </ul>
+{% endblock %}
+ +

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. 

+ +
+

Note: Vous pouvez constatez simplement que les balises de gabarit (fonctions) et les baslises de variables sont entre accolades ; double accolades pour uen variable (\{{ num_books }}), et simple accolade avec le pourcentage ({% extends "base_generic.html" %}) pour les balises.

+
+ +

Gardez en mémoire que les variables utilisées dans les gabarits sont des clés d'un dictionnaires context transmis à la fonction render() de la vue (revenez à l'exemple plus haut, ou l'extrait ci-dessous). La fonction render() traitera le dictionnaire pour restituer uen page HTML où les variables nommées auront été remplacées par leur valeur dans le dictionnaire.

+ +
context = {
+    'num_books': num_books,
+    'num_instances': num_instances,
+    'num_instances_available': num_instances_available,
+    'num_authors': num_authors,
+}
+
+return render(request, 'index.html', context=context)
+ +

Traiter les fichiers statiques dans les gabarits

+ +

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 STATIC_URL. Le paramétrage par défaut du site web définit la variable STATIC_URL à '/static/', mais vous pouvez choisir d'heberger ailleurs.

+ +

Au sein du gabarit, vous ferrez appel à la balise load en précisant "static" pour faire votre ajout, comme décrits dans l'extrait ci-dessous. Vous utilisez la balise static et vous spécifiez ensuite l'URL pour accéder au fichier nécessaire.

+ +
<!-- Add additional CSS in static file -->
+{% load static %}
+<link rel="stylesheet" href="{% static 'css/styles.css' %}">
+ +

De la même manière, vous pouvez par exemple :

+ +
{% load static %}
+<img src="{% static 'catalog/images/local_library_model_uml.png' %}" alt="UML diagram" style="width:555px;height:540px;">
+
+ +
+

Note : 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 (/locallibrary/locallibrary/urls.py) à la création du squelette du site. Cependant nous devrons travailler plus tard la mise en production.

+
+ +

Pour plus de détails sur les fichiers statiques vous pouvez consulter la documentation Django sur la gestion des fichiers statiques.

+ +

Associer des URL

+ +

L'exemple ci-dessous introduit l'utilisation de la balise de gabarit url.

+ +
<li><a href="{% url 'index' %}">Home</a></li>
+
+ +

Cette balise accepte des référence enregistrées par la fonction path() appelée dans les fichiers  urls.py 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.

+ +

Où trouver les gabarits...

+ +

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 settings.py comme indiqué en gras ci-dessous :

+ +
TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [
+            os.path.join(BASE_DIR, 'templates'),
+        ],
+        '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',
+            ],
+        },
+    },
+]
+ +

A quoi cela devrait-il ressembler ?

+ +

A ce niveau, nous avons créé l'ensemble des ressources nécessaires à l'affichage de la page d'accueil. Démarrez le serveur (python3 manage.py runserver) et accédez avec votre navigateur à la page d'accueil du site web http://127.0.0.1:8000/. Si tout va bien, vous devriez avoir une page qui ressemble à celle ci-dessous.

+ +

Page d'accueil en Français

+ +
+

Note: Toutes les ressources n'ayant pas été encore créées les liens vers Tous les livres et Tous les auteurs ne fonctionnent pas encore.

+
+ +

Défi

+ +

Voici deux suggestion pour tester votre connaissance de Django et des requêtes, vues et gabarits :

+ +
    +
  1. La bibliothèque locale dispose d'un gabarit d'origine qui inclu une section title. Surchargez cette section dans le gabarit index et créer un nouveau titre. + +
    +

    Note : La section Concevoir un gabarit détaille la manière de modifier une section.

    +
    +
  2. +
  3. 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  context. Pour faire cela utilisez les variables num_books et num_instances_available. Ensuite vous pourrez mettre à jour le gabarit de la page d'accueil.
    +  
  4. +
+ + + +

Résumé

+ +

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.

+ +

Nous allons nous appuyer sur ces éléments pour concevoir dans le prochain chapitres les 4 pages qui manquent.

+ +

À voir aussi

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}

+ +

Dans ce module

+ + diff --git a/files/fr/learn/server-side/django/index.html b/files/fr/learn/server-side/django/index.html deleted file mode 100644 index fb38612c78..0000000000 --- a/files/fr/learn/server-side/django/index.html +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Django Web Framework (Python) -slug: Learn/Server-side/Django -tags: - - Apprendre - - Débutant - - Python - - django -translation_of: Learn/Server-side/Django ---- -
{{LearnSidebar}}
- -

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.

- -

Prerequis

- -

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 module d'initiation à la programmation web coté serveur.

- -

Une connaissance générale en programmation et plus précisement en Python est recommandée, mais pas nécessaire pour comprendre la majeure partie de ce module.

- -
-

Note : 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 Python pour les non-programmeurs dans la documentation sur le site officiel de Python: python.org).

-
- -

Guides

- -
-
Introduction à Django
-
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.
-
Installer un environnement de développement pour Django
-
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.
-
Tutoriel Django: Le site web d'une librairie
-
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.
-
Tutoriel Django Partie 2: Créer un squelette d'un site web
-
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.
-
Tutoriel Django Partie 3: Utilisation des modèles
-
Cet article montre comment définir des modèles pour le site web que nous appelleront LocalLibrary — 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.
-
Tutoriel Django Partie 4: L'administration d'un site sous Django
-
Maintenant que nous avons créé quelques modèles pour le site web LocalLibrary , 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.
-
Tutoriel Django Partie 5: Céez votre page d'accueil.
-
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 LocalLibrary 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.
-
Tutoriel Django Partie 6: Listes génériques et détails des pages
-
Ce tutoriel viens étendre notre site LocaLibrary 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.
-
Tutoriel Django Partie 7: Les sessions de framework
-
Ce tutoriel viens compléter le site LocalLibrary, 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.
-
Tutoriel Django Partie 8: L'authentification de l'utilisateur ainsi que les permissions
-
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 permissions accordées et de s'ils sont connectés ou non. Comme partie de cette démonstration, nous allons étendre le site LocalLibrary 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é.
-
Tutoriel Django Partie 9: Travailler avec les formulaires
-
Dans ce tutoriel, nous allons vous montrer comment travailler avec les formulaires en HTML 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 LocalLibrary 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).
-
Tutoriel Django Partie 10: Tester une application Django
-
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.
-
Tutoriel Django Partie 11: Déployer son site fait avec Django
-
Vous avez créé (et testé) un incroyable site web LocalLibray, 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.
-
Le module de sécurité de Django
-
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 sécurité web — 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.
-
- -

Evaluation

- -

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.

- -
-
Mini blog avec Django
-
Dans ce devoir, vous utiliserez les connaissances que vous venez d'acquérir, afin de créer votre propre blog.
-
diff --git a/files/fr/learn/server-side/django/index.md b/files/fr/learn/server-side/django/index.md new file mode 100644 index 0000000000..fb38612c78 --- /dev/null +++ b/files/fr/learn/server-side/django/index.md @@ -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 +--- +
{{LearnSidebar}}
+ +

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.

+ +

Prerequis

+ +

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 module d'initiation à la programmation web coté serveur.

+ +

Une connaissance générale en programmation et plus précisement en Python est recommandée, mais pas nécessaire pour comprendre la majeure partie de ce module.

+ +
+

Note : 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 Python pour les non-programmeurs dans la documentation sur le site officiel de Python: python.org).

+
+ +

Guides

+ +
+
Introduction à Django
+
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.
+
Installer un environnement de développement pour Django
+
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.
+
Tutoriel Django: Le site web d'une librairie
+
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.
+
Tutoriel Django Partie 2: Créer un squelette d'un site web
+
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.
+
Tutoriel Django Partie 3: Utilisation des modèles
+
Cet article montre comment définir des modèles pour le site web que nous appelleront LocalLibrary — 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.
+
Tutoriel Django Partie 4: L'administration d'un site sous Django
+
Maintenant que nous avons créé quelques modèles pour le site web LocalLibrary , 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.
+
Tutoriel Django Partie 5: Céez votre page d'accueil.
+
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 LocalLibrary 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.
+
Tutoriel Django Partie 6: Listes génériques et détails des pages
+
Ce tutoriel viens étendre notre site LocaLibrary 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.
+
Tutoriel Django Partie 7: Les sessions de framework
+
Ce tutoriel viens compléter le site LocalLibrary, 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.
+
Tutoriel Django Partie 8: L'authentification de l'utilisateur ainsi que les permissions
+
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 permissions accordées et de s'ils sont connectés ou non. Comme partie de cette démonstration, nous allons étendre le site LocalLibrary 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é.
+
Tutoriel Django Partie 9: Travailler avec les formulaires
+
Dans ce tutoriel, nous allons vous montrer comment travailler avec les formulaires en HTML 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 LocalLibrary 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).
+
Tutoriel Django Partie 10: Tester une application Django
+
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.
+
Tutoriel Django Partie 11: Déployer son site fait avec Django
+
Vous avez créé (et testé) un incroyable site web LocalLibray, 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.
+
Le module de sécurité de Django
+
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 sécurité web — 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.
+
+ +

Evaluation

+ +

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.

+ +
+
Mini blog avec Django
+
Dans ce devoir, vous utiliserez les connaissances que vous venez d'acquérir, afin de créer votre propre blog.
+
diff --git a/files/fr/learn/server-side/django/introduction/index.html b/files/fr/learn/server-side/django/introduction/index.html deleted file mode 100644 index 6fa1007ece..0000000000 --- a/files/fr/learn/server-side/django/introduction/index.html +++ /dev/null @@ -1,277 +0,0 @@ ---- -title: Introduction à Django -slug: Learn/Server-side/Django/Introduction -tags: - - Débutant - - Python - - django -translation_of: Learn/Server-side/Django/Introduction ---- -
{{LearnSidebar}}
- -
{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}
- -

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) .

- - - - - - - - - - - - -
Prérequis:Connaissances basiques de programmation. Une compréhension générale de la programmation coté serveur ainsi qu'une compréhension des interactions client-serveur dans les sites web.
Objectif: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.
- -

Qu'est ce que Django?

- -

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. 

- -

Django vous aide à écrire une application qui est:

- -
-
Complète
-
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 documentation complète et à jour.
-
Polyvalent
-
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!
-
- 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.
-
Sécurisé
-
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.
-
- Un mot de passé haché est une valeur dont la longueur est fixée, créée en envoyant le mot de passe à travers une fonction de hachage cryptographique. 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.
-
- 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 Website security pour plus de détails sur ce genre d'attaques).
-
Scalable
-
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).
-
Maintenable
-
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)).
-
Portable
-
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.
-
- -

D'où vient-il ?

- -

À 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.

- -

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). 

- -
-

Note : Consultez les notes de publication 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.

-
- -

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. 

- -

À quel point Django est-il populaire ?

- -

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 Hot Frameworks 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 ? 

- -

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 !

- -

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 : Page d'accueil de Django).

- -

Django est-il restrictif ?

- -

Les frameworks web sont souvent qualifiés de "restrictifs" ou de "non-restrictifs".

- -

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 dans un domaine particulier (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.

- -

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.
-
- 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.

- -

À quoi ressemble le code Django ?

- -

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 POST data ou GET 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.

- -

Les applications web Django regroupent généralement le code qui gère chacune de ces étapes dans des fichiers séparés :

- -

- - - -
-

Note : Django mentionne cette organisation sous le nom d'architecture "Modèle Vue Template". Elle a plusieurs similarités avec l'architecture Modèle Vue Contrôleur.

-
- - - -

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).

- -

Envoyer la requête à la bonne vue (urls.py)

- -

Le mapper URL est généralement stocké dans un fichier nommé urls.py. Dans l'exemple ci-dessous, le mapper (urlpatterns) définit une liste de mappings entre des routes (des patterns d'URL spécifiqueset 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.

- -
urlpatterns = [
-    path('admin/', admin.site.urls),
-    path('book/<int:id>/', views.book-detail, name='book-detail'),
-    path('catalog/', include('catalog.urls')),
-    re_path(r'^([0-9]+)/$', views.best),
-]
-
- -

L'objet urlpatterns est une liste de fonctions path() et/ou re_path()(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 virgule de traîne optionnelle. Par exemple : [item1, item2, item3,]).

- -

Le premier argument de chaque méthode est une route (pattern) qui sera reconnu.
- La méthode path() 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 re_path() 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 !

- -

Le second argument est une autre fonction qui sera appelée quand le pattern sera reconnu. La notation views.book-detail indique que la fonction s'appelle book-detail() , et qu'elle se trouve dans un module appelé views (i.e. dans un fichier intitulé views.py)

- -

Traiter la requête (views.py)

- -

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.

- -

L'exemple ci-dessous montre une fonction vue minimale index(), 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  HttpRequest comme paramètre (request) et renvoie un objet HttpResponse. 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.

- -
## 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!')
-
- -
-

Note : Un peu de Python :

- - -
- - - -

Les vues sont généralement stockées dans un fichier nommé views.py.

- -

Définir les modèles de données (models.py)

- -

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 types 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.

- -

L'extrait de code ci-dessous montre un modèle Django très simple pour un objet Team. La classe Team est dérivée de la classe Django models.Model. 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 team_level 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. 

- -
# 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')
-
- -
-

Note : Un peu de Python :

- - -
- -

Requêter les données (views.py)

- -

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"). 

- -

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 team_level comprend strictement le texte 'U09' (notez comment ce critère est passé dans la fonction filter() comme argument, où le nom du champ et le type de correspondance sont séparés par un double underscore : team_level__exact).

- -
## nom du fichier : views.py
-
-from django.shortcuts import render
-from .models import Team
-
-def index(request):
-    list_teams = Team.objects.filter(team_level__exact="U09")
-    context = {'youngest_teams': list_teams}
-    return render(request, '/best/index.html', context)
-
- -
-
- -

Cette fonction utilise la fonction render() pour créer la HttpResponse qui est renvoyée au navigateur. Cette fonction est un raccourci; 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 "context"). Dans la prochaine section, nous vous montrons comment des données sont insérées dans le template pour générer le HTML.

- -

Renvoyer les données (templates HTML)

- -

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). 

- -

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 render(). Ce template a été écrit avec l'hypothèse qu'il aurait accès à une liste de variables appelées youngest_teams lorsqu'il est généré (contenu dans la variable context dans la fonction render() ci-dessus). Dans le squelette HTML nous avons une expression qui vérifie tout d'abord que la variable youngest_teams existe, puis itère dessus dans une boucle for . À chaque itération, le template affiche la valeur du team_name de chaque équipe dans un élément {{htmlelement("li")}}.

- -
## nom du fichier : best/templates/best/index.html
-
-<!DOCTYPE html>
-<html lang="en">
-<body>
-
- {% if youngest_teams %}
-    <ul>
-    {% for team in youngest_teams %}
-        <li>\{\{ team.team_name \}\}</li>
-    {% endfor %}
-    </ul>
-{% else %}
-    <p>No teams are available.</p>
-{% endif %}
-
-</body>
-</html>
- -

Que pouvez-vous faire d'autre ?

- -

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 :

- - - -

Sommaire

- -

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.

- -

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.

- -
{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}
- -

In this module

- - diff --git a/files/fr/learn/server-side/django/introduction/index.md b/files/fr/learn/server-side/django/introduction/index.md new file mode 100644 index 0000000000..6fa1007ece --- /dev/null +++ b/files/fr/learn/server-side/django/introduction/index.md @@ -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 +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}
+ +

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) .

+ + + + + + + + + + + + +
Prérequis:Connaissances basiques de programmation. Une compréhension générale de la programmation coté serveur ainsi qu'une compréhension des interactions client-serveur dans les sites web.
Objectif: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.
+ +

Qu'est ce que Django?

+ +

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. 

+ +

Django vous aide à écrire une application qui est:

+ +
+
Complète
+
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 documentation complète et à jour.
+
Polyvalent
+
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!
+
+ 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.
+
Sécurisé
+
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.
+
+ Un mot de passé haché est une valeur dont la longueur est fixée, créée en envoyant le mot de passe à travers une fonction de hachage cryptographique. 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.
+
+ 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 Website security pour plus de détails sur ce genre d'attaques).
+
Scalable
+
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).
+
Maintenable
+
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)).
+
Portable
+
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.
+
+ +

D'où vient-il ?

+ +

À 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.

+ +

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). 

+ +
+

Note : Consultez les notes de publication 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.

+
+ +

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. 

+ +

À quel point Django est-il populaire ?

+ +

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 Hot Frameworks 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 ? 

+ +

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 !

+ +

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 : Page d'accueil de Django).

+ +

Django est-il restrictif ?

+ +

Les frameworks web sont souvent qualifiés de "restrictifs" ou de "non-restrictifs".

+ +

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 dans un domaine particulier (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.

+ +

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.
+
+ 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.

+ +

À quoi ressemble le code Django ?

+ +

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 POST data ou GET 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.

+ +

Les applications web Django regroupent généralement le code qui gère chacune de ces étapes dans des fichiers séparés :

+ +

+ + + +
+

Note : Django mentionne cette organisation sous le nom d'architecture "Modèle Vue Template". Elle a plusieurs similarités avec l'architecture Modèle Vue Contrôleur.

+
+ + + +

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).

+ +

Envoyer la requête à la bonne vue (urls.py)

+ +

Le mapper URL est généralement stocké dans un fichier nommé urls.py. Dans l'exemple ci-dessous, le mapper (urlpatterns) définit une liste de mappings entre des routes (des patterns d'URL spécifiqueset 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.

+ +
urlpatterns = [
+    path('admin/', admin.site.urls),
+    path('book/<int:id>/', views.book-detail, name='book-detail'),
+    path('catalog/', include('catalog.urls')),
+    re_path(r'^([0-9]+)/$', views.best),
+]
+
+ +

L'objet urlpatterns est une liste de fonctions path() et/ou re_path()(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 virgule de traîne optionnelle. Par exemple : [item1, item2, item3,]).

+ +

Le premier argument de chaque méthode est une route (pattern) qui sera reconnu.
+ La méthode path() 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 re_path() 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 !

+ +

Le second argument est une autre fonction qui sera appelée quand le pattern sera reconnu. La notation views.book-detail indique que la fonction s'appelle book-detail() , et qu'elle se trouve dans un module appelé views (i.e. dans un fichier intitulé views.py)

+ +

Traiter la requête (views.py)

+ +

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.

+ +

L'exemple ci-dessous montre une fonction vue minimale index(), 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  HttpRequest comme paramètre (request) et renvoie un objet HttpResponse. 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.

+ +
## 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!')
+
+ +
+

Note : Un peu de Python :

+ + +
+ + + +

Les vues sont généralement stockées dans un fichier nommé views.py.

+ +

Définir les modèles de données (models.py)

+ +

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 types 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.

+ +

L'extrait de code ci-dessous montre un modèle Django très simple pour un objet Team. La classe Team est dérivée de la classe Django models.Model. 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 team_level 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. 

+ +
# 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')
+
+ +
+

Note : Un peu de Python :

+ + +
+ +

Requêter les données (views.py)

+ +

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"). 

+ +

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 team_level comprend strictement le texte 'U09' (notez comment ce critère est passé dans la fonction filter() comme argument, où le nom du champ et le type de correspondance sont séparés par un double underscore : team_level__exact).

+ +
## nom du fichier : views.py
+
+from django.shortcuts import render
+from .models import Team
+
+def index(request):
+    list_teams = Team.objects.filter(team_level__exact="U09")
+    context = {'youngest_teams': list_teams}
+    return render(request, '/best/index.html', context)
+
+ +
+
+ +

Cette fonction utilise la fonction render() pour créer la HttpResponse qui est renvoyée au navigateur. Cette fonction est un raccourci; 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 "context"). Dans la prochaine section, nous vous montrons comment des données sont insérées dans le template pour générer le HTML.

+ +

Renvoyer les données (templates HTML)

+ +

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). 

+ +

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 render(). Ce template a été écrit avec l'hypothèse qu'il aurait accès à une liste de variables appelées youngest_teams lorsqu'il est généré (contenu dans la variable context dans la fonction render() ci-dessus). Dans le squelette HTML nous avons une expression qui vérifie tout d'abord que la variable youngest_teams existe, puis itère dessus dans une boucle for . À chaque itération, le template affiche la valeur du team_name de chaque équipe dans un élément {{htmlelement("li")}}.

+ +
## nom du fichier : best/templates/best/index.html
+
+<!DOCTYPE html>
+<html lang="en">
+<body>
+
+ {% if youngest_teams %}
+    <ul>
+    {% for team in youngest_teams %}
+        <li>\{\{ team.team_name \}\}</li>
+    {% endfor %}
+    </ul>
+{% else %}
+    <p>No teams are available.</p>
+{% endif %}
+
+</body>
+</html>
+ +

Que pouvez-vous faire d'autre ?

+ +

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 :

+ + + +

Sommaire

+ +

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.

+ +

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.

+ +
{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}
+ +

In this module

+ + diff --git a/files/fr/learn/server-side/django/models/index.html b/files/fr/learn/server-side/django/models/index.html deleted file mode 100644 index 18311a5a9f..0000000000 --- a/files/fr/learn/server-side/django/models/index.html +++ /dev/null @@ -1,462 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}
- -

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.

- - - - - - - - - - - - -
Pré-requis: -

Django didactique Section 2: Créer le squelette du site web.

-
Objectif: -

Concevoir et créer vos propres modèles de données et choisir les attributs idoines.

-
- -

Survol

- -

Les applications web Django donnent accès aux données enregistrées dans une base à l'aide d'une classe d'objets models. Une classe d'objet héritée de models définit une structure de données 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.

- -

Ce chapitre vous montre sur la base du site web d'une bibliothèque locale comment concevoir, créer et manipuler les données par l'intermédiaire du modèle de données.

- -

Concevoir le modèle de données de la bibliothèque locale

- -

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.

- -

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.

- -

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.

- -

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.).

- -

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 (OneToOneField), les relations un à n qui partage l'appartenance d'un objet à avec d'autres (ForeignKey) et les relations n à n qui associent des groupes d'objets entre-eux (ManyToManyField).

- -

Avec ces éléments présents à l'esprit, le diagramme  de classes UML ci-dessous décrit les objets de la bibliothèque.

- -

LocalLibrary Model UML

- -

Le modèle ainsi créé, décrit l'objet livre - Book - avec une description générique d'un livre, la copie d'un livre - BookInstance - avec l'état d'un copie physique d'un livre et de sa disponibilité, et l'objet auteur - Author. 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 - BookInstance:status sera géré dans le code Django LOAN_STATUS. 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.

- -

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.

- -
-

Note : 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.

-
- -

Introduction au modèle de données

- -

Cette section fournit une rapide introduction à la définition des objets de conception du modèle de données. 

- -

Spécification

- -

Les objets sont toujours définis dans le fichier models.py de chaque application. Ils sont conçus comme sous-classe de django.db.models.Model, 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 MyModelName:

- -
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
- -

Détaillons ce qu'il en retourne :

- -

Champs ou attributs

- -

Chaque objet peut contenir autant d'attributs que de besoin et de quelque type qu'il soit. Chaque attribut correspondra à une colonne - ou champ - 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 :

- -
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
- -

Dans l'exemple ci-dessus, le champs est une chaîne de caractères — de type models.CharField —  dont le nom est my_field_name. 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 :

- - - -

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 (verbose_name) ou ils seront déduits à l'aide d'un changement de casse ou le remplacement des espaces par des soulignés (comme par exemple my_field_name serait l'étiquette par défaut du champs My field name).

- -

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.

- -
Les arguments courants des champs
- -

Vous trouverez ci-dessous les arguments les plus utilisés dans la définition des champs :

- - - -

L'ensemble des options de champs peut être consulté sur le site Django.

- -
Les types courrants de champs
- -

Vous trouverez ci-dessous les arguments les principaux type de champs :

- - - -

L'ensemble des types de champs peut être consulté sur le site Django.

- -

Métadonnées

- -

Vous avez la capacité de déclarer des métadonnées à l'aide de la classe  class Meta, comme précisé ci-dessous :

- -
class Meta:
-    ordering = ['-my_field_name']
-
- -

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 enregistrements. Vous l'obtenez en précisant la liste des champs dans l'attribut ordering 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 moins (-) alors le classement sera naturellement inversé.

- -

Voici un exemple de classe de livre par titre et dates de parution :

- -
ordering = ['title', '-pubdate']
- -

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.

- -

Un autre attribut très utile est celui d'un nom vernaculaire pour la classe, verbose_name  peut être au singulier et au pluriel :

- -
verbose_name = 'BetterName'
- -

D'autres attributs vous permettent de compléter des droits d'accès à ceux appliqués par défaut, des classements 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 enregistrements, mais servira de support à d'autres classes partageant des éléments communs).

- -

D'autres éléments sont aussi disponibles pour contrôler le comportement d'une base de données, mais sont principalement utilisés pour appliquer le modèle ORM sur une base de données déjà existante.

- -

L'ensemble des métadonnées de classe peut être consulté sur le site Django.

- -

Méthodes

- -

Comme tout objet Python, une classe héritée de model peut utiliser des méthodes.

- -

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 __str__() pour permettre d'afficher un élément compréhensible qui représentera l'instance de la classe. 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.

- -
def __str__(self):
-    return self.field_name
- -

Une seconde méthode très utilisée dans le cadriciel Django est get_absolute_url(). 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 get_absolute_url() est de la forme :

- -
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)])
-
- -
-

Note : En supposant que vous allez utiliser des URLs du type /myapplication/mymodelname/2 pour afficher individuellement les données des enregistrements de la table associée à votre modèle de données (où "2" est l'identifiant 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 reverse() a pour objectif d'écrire l'URL dans un format cohérent avec le traitement des URL par les navigateurs.

- -

Bien entendu, cela requiert d'écrire le routage de l'URL, la vue et le gabarit...

-
- -

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.

- -

Administration des données

- -

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 sera revu progressivement dans les avancées de l'application Bibliothèque.

- -

Créer et modifier des enregistrements

- -

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 save().

- -
# 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()
-
- -
-

Note : Si aucun champs n'a été défini comme une clé primaire (option primary_key), un champs nommé id ou pk 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.

-
- -

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 save() pour enregistrer en base de données les modifications.

- -
# 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()
- -

Rechercher des enregistrements

- -

La classe de base objects permet de faire des recherches d'enregistrement qui correspondront aux critères de recherche souhaités.

- -
-

Note : Nous utiliserons dans les explications le modèle de données d'un livre (Book)avec des titres (title) et des genres littéraires (genre), car expliquer la manière de rechercher sur un modèle théorique n'est pas très pédagogique.

-
- -

Vous pouvez obtenir tous les enregistrements d'un modèle de données sous la forme d'un jeu de données ou QuerySet, en utilisant objects.all(). Un QuerySet est un objet itérable, c'est-à-dire jeu de données contenant des objets que l'on peut parcourir.

- -
all_books = Book.objects.all()
-
- -

Un filtre Django ou filter() 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, nous filtrons les livres dont le titre contient le mot "wild", puis nous dénombrons le jeu de données.

- -
wild_books = Book.objects.filter(title__contains='wild')
-number_wild_books = wild_books.count()
-
- -

Les arguments passés en option sont le champs et la nature du contrôle à effectuer. On utilise le format : field_name__match_type : dans l'exemple ci-dessus, le double sous-ligné marque la séparation entre le champ title et le type de contrôle contains ; concrètement, le filtre est appliqué sur le champ title contenant le mot wild en respectant la casse. Il existe d'autres options de contrôle : icontains (sans respect de la casse), iexact (le champs correspond exactement à la valeur donnée sans respect de la casse), exact (idem en respectant la casse) et in, gt (plus grand que), startswith(commence par), etc. La liste complète est consultable sur la documentation de Django.

- -

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 (ForeignKey). 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 (name) du genre (genre) du livre.

- -
# Le critère s'appliquera sur les genres contenant 'fiction' i.e. : Fiction, Science fiction, non-fiction etc.
-books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')
-
- -
-

Note : Vous pouvez construire une chemin pour naviguer dans autant de niveaux de relation (ForeignKey/ManyToManyField) que vous en avez besoin en concaténant des noms de champs à l'aide  (__) . Si par exemple vous souhaitez trouver un livre (Book) qui possède différents type (type) de couvertures (cover) identifiées par des noms (name) alors le chemin sera du type : type__cover__name__exact='hard'.

-
- -

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 les requêtes sur le site de référence de Django.

- -

Définition du modèle de données de l'application LocalLibrary

- -

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 models.py présent dans le répertoire /locallibrary/catalog/. 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.

- -
from django.db import models
-
-# Create your models here.
- -

L'objet Genre

- -

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 models.py.

- -
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
- -

L'objet, en relation avec la base de données, possède un seul attribut (name) de type chaîne de caractères (CharField), qui sera utilisé pour décrire le genre d'un livre (limité à 200 caractères). Une option (help_text) permet d'utiliser une étiquettes d'aide dans les pages et formulaires du site web. La méthode __str__(), qui retourne simplement le nom du genre littéraire de chaque enregistrement. Puisque qu'il n'y a pas de nom vernaculaire (verbose_name), le champ sera simplement nommé Name dans les formulaires.

- -

L'objet Book

- -

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.

- -
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 <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
-
-    # 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)])
-
- -

Le genre littéraire est une relation n à n (ManyToManyField) car un livre peut avoir plusieurs genres 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 (ForeignKey) de telle sorte qu'un livre n'a qu'un seul auteur et une auteur peut avoir écrit plusieurs livres.

- -

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 null positionné à True permet d'avoir un contenu vide en base de données, la second option on_delete=models.SET_NULL 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.

- -

Deux méthodes sont déclarées pour cet objet. La méthode __str__() obligatoirement requise par Django pour manipuler les instances d'objet et les enregistrements associés en base. La seconde méthode, get_absolute_url(), 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  book-detail, et nous aurons à définir une vue et un gabarit.

- -

L'objet BookInstance

- -

Occupons nous maintenant de l'objet BookInstance. Comme précédemment, copiez le contenu décrivant l'objet BookInstance ci-dessous dans votre fichier models.py. 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.

- -

Les attributs et méthodes vont vous paraître familiers. On utilise :

- - - -
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})'
- -

De nouveaux types de champs sont utilisés :

- - - -

La méthode __str__() 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é.

- -
-

Note : Un aspect de Python:

- - -
- -

L'objet Author

- -

Terminons en copiant la description de l'objet Author à la fin du fichier models.py.

- -
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}'
-
- -

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 (__str__()) en retournant les nom et prénom de l'auteur dans cet ordre, et, l'autre (get_absolute_url()) permettra de publier les informations propres à chaque auteur.

- -

Appliquer les modifications en base

- -

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.

- -
python3 manage.py makemigrations
-python3 manage.py migrate
- -

Défi — Introduire les langues

- -

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.

- -

Gardez en tête que :

- - - -

Après avoir fait vos choix, modéliser le et ajouter les champs utiles. Vous pouvez ensuite voir sur Github nous l'avons fait.

- -

Une dernière chose... n'oubliez pas d'appliquer les modifications en base de données

- -
python3 manage.py makemigrations
-python3 manage.py migrate
- -

Résumé

- -

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.

- -

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.

- -

A voir aussi

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/server-side/django/models/index.md b/files/fr/learn/server-side/django/models/index.md new file mode 100644 index 0000000000..18311a5a9f --- /dev/null +++ b/files/fr/learn/server-side/django/models/index.md @@ -0,0 +1,462 @@ +--- +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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}
+ +

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.

+ + + + + + + + + + + + +
Pré-requis: +

Django didactique Section 2: Créer le squelette du site web.

+
Objectif: +

Concevoir et créer vos propres modèles de données et choisir les attributs idoines.

+
+ +

Survol

+ +

Les applications web Django donnent accès aux données enregistrées dans une base à l'aide d'une classe d'objets models. Une classe d'objet héritée de models définit une structure de données 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.

+ +

Ce chapitre vous montre sur la base du site web d'une bibliothèque locale comment concevoir, créer et manipuler les données par l'intermédiaire du modèle de données.

+ +

Concevoir le modèle de données de la bibliothèque locale

+ +

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.

+ +

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.

+ +

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.

+ +

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.).

+ +

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 (OneToOneField), les relations un à n qui partage l'appartenance d'un objet à avec d'autres (ForeignKey) et les relations n à n qui associent des groupes d'objets entre-eux (ManyToManyField).

+ +

Avec ces éléments présents à l'esprit, le diagramme  de classes UML ci-dessous décrit les objets de la bibliothèque.

+ +

LocalLibrary Model UML

+ +

Le modèle ainsi créé, décrit l'objet livre - Book - avec une description générique d'un livre, la copie d'un livre - BookInstance - avec l'état d'un copie physique d'un livre et de sa disponibilité, et l'objet auteur - Author. 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 - BookInstance:status sera géré dans le code Django LOAN_STATUS. 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.

+ +

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.

+ +
+

Note : 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.

+
+ +

Introduction au modèle de données

+ +

Cette section fournit une rapide introduction à la définition des objets de conception du modèle de données. 

+ +

Spécification

+ +

Les objets sont toujours définis dans le fichier models.py de chaque application. Ils sont conçus comme sous-classe de django.db.models.Model, 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 MyModelName:

+ +
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
+ +

Détaillons ce qu'il en retourne :

+ +

Champs ou attributs

+ +

Chaque objet peut contenir autant d'attributs que de besoin et de quelque type qu'il soit. Chaque attribut correspondra à une colonne - ou champ - 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 :

+ +
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
+ +

Dans l'exemple ci-dessus, le champs est une chaîne de caractères — de type models.CharField —  dont le nom est my_field_name. 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 :

+ + + +

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 (verbose_name) ou ils seront déduits à l'aide d'un changement de casse ou le remplacement des espaces par des soulignés (comme par exemple my_field_name serait l'étiquette par défaut du champs My field name).

+ +

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.

+ +
Les arguments courants des champs
+ +

Vous trouverez ci-dessous les arguments les plus utilisés dans la définition des champs :

+ + + +

L'ensemble des options de champs peut être consulté sur le site Django.

+ +
Les types courrants de champs
+ +

Vous trouverez ci-dessous les arguments les principaux type de champs :

+ + + +

L'ensemble des types de champs peut être consulté sur le site Django.

+ +

Métadonnées

+ +

Vous avez la capacité de déclarer des métadonnées à l'aide de la classe  class Meta, comme précisé ci-dessous :

+ +
class Meta:
+    ordering = ['-my_field_name']
+
+ +

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 enregistrements. Vous l'obtenez en précisant la liste des champs dans l'attribut ordering 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 moins (-) alors le classement sera naturellement inversé.

+ +

Voici un exemple de classe de livre par titre et dates de parution :

+ +
ordering = ['title', '-pubdate']
+ +

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.

+ +

Un autre attribut très utile est celui d'un nom vernaculaire pour la classe, verbose_name  peut être au singulier et au pluriel :

+ +
verbose_name = 'BetterName'
+ +

D'autres attributs vous permettent de compléter des droits d'accès à ceux appliqués par défaut, des classements 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 enregistrements, mais servira de support à d'autres classes partageant des éléments communs).

+ +

D'autres éléments sont aussi disponibles pour contrôler le comportement d'une base de données, mais sont principalement utilisés pour appliquer le modèle ORM sur une base de données déjà existante.

+ +

L'ensemble des métadonnées de classe peut être consulté sur le site Django.

+ +

Méthodes

+ +

Comme tout objet Python, une classe héritée de model peut utiliser des méthodes.

+ +

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 __str__() pour permettre d'afficher un élément compréhensible qui représentera l'instance de la classe. 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.

+ +
def __str__(self):
+    return self.field_name
+ +

Une seconde méthode très utilisée dans le cadriciel Django est get_absolute_url(). 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 get_absolute_url() est de la forme :

+ +
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)])
+
+ +
+

Note : En supposant que vous allez utiliser des URLs du type /myapplication/mymodelname/2 pour afficher individuellement les données des enregistrements de la table associée à votre modèle de données (où "2" est l'identifiant 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 reverse() a pour objectif d'écrire l'URL dans un format cohérent avec le traitement des URL par les navigateurs.

+ +

Bien entendu, cela requiert d'écrire le routage de l'URL, la vue et le gabarit...

+
+ +

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.

+ +

Administration des données

+ +

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 sera revu progressivement dans les avancées de l'application Bibliothèque.

+ +

Créer et modifier des enregistrements

+ +

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 save().

+ +
# 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()
+
+ +
+

Note : Si aucun champs n'a été défini comme une clé primaire (option primary_key), un champs nommé id ou pk 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.

+
+ +

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 save() pour enregistrer en base de données les modifications.

+ +
# 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()
+ +

Rechercher des enregistrements

+ +

La classe de base objects permet de faire des recherches d'enregistrement qui correspondront aux critères de recherche souhaités.

+ +
+

Note : Nous utiliserons dans les explications le modèle de données d'un livre (Book)avec des titres (title) et des genres littéraires (genre), car expliquer la manière de rechercher sur un modèle théorique n'est pas très pédagogique.

+
+ +

Vous pouvez obtenir tous les enregistrements d'un modèle de données sous la forme d'un jeu de données ou QuerySet, en utilisant objects.all(). Un QuerySet est un objet itérable, c'est-à-dire jeu de données contenant des objets que l'on peut parcourir.

+ +
all_books = Book.objects.all()
+
+ +

Un filtre Django ou filter() 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, nous filtrons les livres dont le titre contient le mot "wild", puis nous dénombrons le jeu de données.

+ +
wild_books = Book.objects.filter(title__contains='wild')
+number_wild_books = wild_books.count()
+
+ +

Les arguments passés en option sont le champs et la nature du contrôle à effectuer. On utilise le format : field_name__match_type : dans l'exemple ci-dessus, le double sous-ligné marque la séparation entre le champ title et le type de contrôle contains ; concrètement, le filtre est appliqué sur le champ title contenant le mot wild en respectant la casse. Il existe d'autres options de contrôle : icontains (sans respect de la casse), iexact (le champs correspond exactement à la valeur donnée sans respect de la casse), exact (idem en respectant la casse) et in, gt (plus grand que), startswith(commence par), etc. La liste complète est consultable sur la documentation de Django.

+ +

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 (ForeignKey). 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 (name) du genre (genre) du livre.

+ +
# Le critère s'appliquera sur les genres contenant 'fiction' i.e. : Fiction, Science fiction, non-fiction etc.
+books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')
+
+ +
+

Note : Vous pouvez construire une chemin pour naviguer dans autant de niveaux de relation (ForeignKey/ManyToManyField) que vous en avez besoin en concaténant des noms de champs à l'aide  (__) . Si par exemple vous souhaitez trouver un livre (Book) qui possède différents type (type) de couvertures (cover) identifiées par des noms (name) alors le chemin sera du type : type__cover__name__exact='hard'.

+
+ +

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 les requêtes sur le site de référence de Django.

+ +

Définition du modèle de données de l'application LocalLibrary

+ +

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 models.py présent dans le répertoire /locallibrary/catalog/. 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.

+ +
from django.db import models
+
+# Create your models here.
+ +

L'objet Genre

+ +

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 models.py.

+ +
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
+ +

L'objet, en relation avec la base de données, possède un seul attribut (name) de type chaîne de caractères (CharField), qui sera utilisé pour décrire le genre d'un livre (limité à 200 caractères). Une option (help_text) permet d'utiliser une étiquettes d'aide dans les pages et formulaires du site web. La méthode __str__(), qui retourne simplement le nom du genre littéraire de chaque enregistrement. Puisque qu'il n'y a pas de nom vernaculaire (verbose_name), le champ sera simplement nommé Name dans les formulaires.

+ +

L'objet Book

+ +

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.

+ +
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 <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
+
+    # 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)])
+
+ +

Le genre littéraire est une relation n à n (ManyToManyField) car un livre peut avoir plusieurs genres 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 (ForeignKey) de telle sorte qu'un livre n'a qu'un seul auteur et une auteur peut avoir écrit plusieurs livres.

+ +

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 null positionné à True permet d'avoir un contenu vide en base de données, la second option on_delete=models.SET_NULL 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.

+ +

Deux méthodes sont déclarées pour cet objet. La méthode __str__() obligatoirement requise par Django pour manipuler les instances d'objet et les enregistrements associés en base. La seconde méthode, get_absolute_url(), 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  book-detail, et nous aurons à définir une vue et un gabarit.

+ +

L'objet BookInstance

+ +

Occupons nous maintenant de l'objet BookInstance. Comme précédemment, copiez le contenu décrivant l'objet BookInstance ci-dessous dans votre fichier models.py. 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.

+ +

Les attributs et méthodes vont vous paraître familiers. On utilise :

+ + + +
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})'
+ +

De nouveaux types de champs sont utilisés :

+ + + +

La méthode __str__() 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é.

+ +
+

Note : Un aspect de Python:

+ + +
+ +

L'objet Author

+ +

Terminons en copiant la description de l'objet Author à la fin du fichier models.py.

+ +
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}'
+
+ +

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 (__str__()) en retournant les nom et prénom de l'auteur dans cet ordre, et, l'autre (get_absolute_url()) permettra de publier les informations propres à chaque auteur.

+ +

Appliquer les modifications en base

+ +

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.

+ +
python3 manage.py makemigrations
+python3 manage.py migrate
+ +

Défi — Introduire les langues

+ +

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.

+ +

Gardez en tête que :

+ + + +

Après avoir fait vos choix, modéliser le et ajouter les champs utiles. Vous pouvez ensuite voir sur Github nous l'avons fait.

+ +

Une dernière chose... n'oubliez pas d'appliquer les modifications en base de données

+ +
python3 manage.py makemigrations
+python3 manage.py migrate
+ +

Résumé

+ +

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.

+ +

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.

+ +

A voir aussi

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}

+ +

Dans ce module

+ + diff --git a/files/fr/learn/server-side/django/skeleton_website/index.html b/files/fr/learn/server-side/django/skeleton_website/index.html deleted file mode 100644 index 636b371873..0000000000 --- a/files/fr/learn/server-side/django/skeleton_website/index.html +++ /dev/null @@ -1,403 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}
- -

Ce second article de la série didactique Django 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...

- - - - - - - - - - - - -
Prérequis:Set up a Django development environment. Avoir pris connaissance de l'article précédent.
Objectifs:Être capable d'utiliser les outils de Django pour initier un nouveau projet.
- -

Vue d'ensemble

- -

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.

- -

La création est aisée:

- -
    -
  1. Utilisez la commande django-admin pour créer le dossier du projet ainsi que les sous-dossiers et fichiers de base ainsi que le script de gestion du projet (manage.py).
  2. -
  3. Utilisez manage.py pour créer une ou plusieurs applications du projet. -
    -

    Note : 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.

    -
    -
  4. -
  5. Enregistrez la nouvelle application dans le projet.
  6. -
  7. Liez les urls et chemins pour chaque application.
  8. -
- -

Pour le site web "Bibliothèque locale", le dossier du site web et celui du projet auront le même nom locallibrary. Une seule application catalog sera utilisée. La hiérachie de dossier du projet à la forme ci-dessous :

- -
locallibrary/         # Website foldermanage.py         # Script to run Django tools for this project (created using django-admin)
-    locallibrary/     # Website/project folder (created using django-admin)
-    catalog/          # Application folder (created using manage.py)
-
- -
-

Note : Afin de respecter la cohérence du code et pouvoir utiliser les développements sur GitHub, les noms du site et des applications, en anglais, n'ont pas été modifiés.

-
- -

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.

- -

Créer le projet locallibrary

- -

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 environnement virtuel python, 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 django_projects et déplacez-vous dans ce dernier :

- -
mkdir django_projects
-cd django_projects
- -

Pour créer un nouveau projet avec le quadriciel Django, il suffit d'utiliser la commande  django-admin startproject. 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 :

- -
django-admin startproject locallibrary
-cd locallibrary
- -

La commande django-admin crée une arboresence contenant des fichiers et un sous-dossier portant le même nom que le projet :

- -
locallibrary/
-    manage.py
-    locallibrary/
-        __init__.py
-        settings.py
-        urls.py
-        wsgi.py
- -

Votre répertoire de travail est de la forme :

- -
../django_projects/locallibrary/
- -

Le sous-dossier locallibrary permettra de gérer les requêtes web, il contient :

- - - -

Le fichier manage.py 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.

- -

Créer l'application catalog

- -

La commande ci-dessous va créer l'application catalog. Vous devez être dans le dossier de votre projet locallibrary pour exécuter cette commande (dans le même dossier que le fichier manage.py de votre projet) :

- -
python3 manage.py startapp catalog
- -
-

Note : La commande ci-dessus est exécutée dans un environnement Linux/macOS X. Sous Windows, il se peut que la commande soit : py -3 manage.py startapp catalog

- -

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 python3 par py -3.

- -

Si vous utilisez une version postérieure à la version 3.7.0, la commande devrait désormais être  py manage.py startapp catalog

-
- -

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 views.py, le modèle de données dans models.py, etc.). Ces fichiers contiennent les éléments minimaux nécessaires à leur utilisation dans le projet.

- -

Le dossier projet locallibrary est agrémenté d'un nouveau sous-dossier catalog :

- -
locallibrary/
-    manage.py
-    locallibrary/
-    catalog/
-        admin.py
-        apps.py
-        models.py
-        tests.py
-        views.py
-        __init__.py
-        migrations/
-
- -

A ceci s'ajoute :

- - - -
-

Note : 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.

-
- -

Enregistrer l'application catalog

- -

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 INSTALLED_APPS en ajoutant le nom de l'application à la liste déjà présente.

- -

Éditez le fichier django_projects/locallibrary/locallibrary/settings.py et allez jusqu'à la liste INSTALLED_APPS. Ajoutez alors comme indiqué ci-dessous l'application à la liste.

- -
INSTALLED_APPS = [
-    'django.contrib.admin',
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    'catalog.apps.CatalogConfig', 
-]
- -

Le nouvel enregistrement défini l'objet pour cette nouvelle application avec le nom (CatalogConfig) qui a été généré dans le fichier /locallibrary/catalog/apps.py quand l'application a été créée.

- -
-

Note : Nous verrons plus loin les autres paramètres de ce fichier(comme MIDDLEWARE). Cela permet la prise en charge par Django administration site et donne accès à de nombreuses fonctionnalités (gestion des sessions, de l'authentication, etc).

-
- -

Définir la base de données

- -

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 bases de données prises en charge sont bien décrites sur le site du projet Django.

- -

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 settings.py est nécessaire pour utiliser ce SGBD :

- -
DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
-    }
-}
-
- -

D'autres paramètrages du projet

- -

Le fichier settings.py 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 (Liste des codes - en anglais). Changez la variable TIME_ZONE de votre projet avec la chaîne appropriée à votre fuseau, par exemple :

- -
TIME_ZONE = 'Europe/Paris'
- -

Il y a deux paramètres à connaître, même s'il ne seront pas modifiés pour l'instant :

- - - -

Configurer le routage des URLs

- -

La création du site web s'appuie sur un routage d'URL et une gestion de la cartographie des URLs dans le fichier urls.py) 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.

- -

A l'ouverture du fichier locallibrary/locallibrary/urls.py,  vous pouvez remarquer les premières instructions sur la manière de gérer la cartographie des URLs.

- -
"""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),
-]
-
- -

Le routage des URLs est géré à l'aide de la variable urlpatterns. Elle consititue une liste Python de fonctions path(). Chaque instance path() 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 urlpatterns 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 admin.site.urls.

- -
-

Note : Dans la fonction path(), 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 < et >, par exemple 'catalog/<id>/'). Ce motif correspondra à une URL du type /catalog/des_caracteres/. La chaîne des_caracteres sera transmis à la vue comme une chaîne de caractère associée à une variable nommée id. Ce point sera vu en détails plus loin dans la série didactique.

-
- -

Ajoutez les lignes ci-dessous à la fin du fichier de manière à ajouter dans la variable urlpatterns une nouvelle entrée à la liste des routes. Cette nouvelle entrée permet une nouvelle route pour catalog/ dont la gestion est déléguée au fichier urls.py du module catalog (c'est-à-dire le fichier catalog/urls.py).

- -
# 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')),
-]
-
-
- -

Il est nécessaire de rediriger la racine du site (concrètement https://127.0.0.1:8000/) vers celui de la seule application catalog qui va être utilisée dans ce projet (concrètemen 127.0.0.1:8000/catalog/). Pour cette étape, nous utilisons la fonction particulière (RedirectView) qui prend comme argument le lien relatif (concrètement /catalog/) quand le motif de l'URL correspondra (concrètement la racine du site).

- -

Ajoutez les lignes ci-dessous au bas du fichier urls.py :

- -
#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)),
-]
-
- -

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 maintenez ce mode d'écriture, vous aurez le message ci-dessous au démarrage du serveur :

- -
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 '/'.
-
- -

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. 

- -

Ajoutez les lignes ci-dessous au bas du fichier urls.py :

- -
# 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)
-
- -
-

Note : Il y a plusieurs manière pour ajouter des routes à la variable urlpatterns (dans les étapes décrites ci-dessus nous avons ajouté petit à patir en utilisant l'opérateur += pour bien séparer les étapes). Il est en réalité tout à fait possible de tout regrouper dans une seule étape :

- -
urlpatterns = [
-    path('admin/', admin.site.urls),
-    path('catalog/', include('catalog.urls')),
-    path('', RedirectView.as_view(url='/catalog/')),
-] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
-
- -

De même, nous avons ajouté des imports de module à chaque étapes (par exemple, from django.urls import include) 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.

-
- -

Dernière étape ! Il faut créer le fichier urls.py dans l'application (ou le module) catalog et de définir la variable urlpatterns vide pour le moment. 

- -
from django.urls import path
-from . import views
-
-urlpatterns = [
-
-]
-
- -

Tester le site web

- -

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.

- -

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.

- -

Exécuter la migartion de la base de données

- -

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 locallibrary/catalog/migration) pour modifier les structures de données associées dans la base de données.

- -

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 manage.py):

- -
python3 manage.py makemigrations
-python3 manage.py migrate
-
- -
-

Attention : 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).

-
- -

L'option makemigrations 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.

- -

L'option migrate applique les modifications sur la base de données (Django trace les modifications réalisées dans la base de données).

- -
-

Note : Vous pouvez consulter la documentation Migrations (sur le site Django) pour plus d'informations.

-
- -

Démarrer le site web

- -

Pendant la phase de développement, vous pouvez tester votre serveur sur un mode local et le consulter avec votre navigateur.

- -
-

Note : 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 http://127.0.0.1:8000/. Cependant, vous pouvez modifier ces paramètres et pour plus d'information vous pouvez consulter la documentation sur le site Django des commandes django-admin and manage.py: runserver.

-
- -

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 manage.py) :

- -
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.
-
- -

Dès que le serveur est actif, vous pouvez utiliser votre navigateur est accéder à l'URL http://127.0.0.1:8000/. Vous devriez accéder à la page d'erreur ci-dessous :

- -

Django Debug page for Django 2.0

- -

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 catalog.urls module (que nous avons déclaré dans le fichier urls.py du projet). 

- -
-

Note : 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é.

-
- -

À ce stade, nous pouvons considérer que le serveur fonctionne !

- -
-

Note : 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 !

-
- -

Relevez le défi...

- -

Le dossier catalog/ a été créé automatiquement et contient des fichiers pour les vues, modèles de données, etc. Ouvrez-les pour les consulter. 

- -

Comme vous avez pu le constatez plus haut, une route pour l'administration du site (http://127.0.0.1:8000/admin/) existe déjà dans le fichier urls.py du projet. Avec votre navigateur web, vous pouvez découvrir ce qui est derrière ce site.

- - - -

Résumé

- -

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.

- -

Maintenant que ceci est fait, le site web Local Library est opérationnel et nous allons passer à la partie codage et développement pour que le site produise ce qu'il est censé faire.

- -

A voir aussi...

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/server-side/django/skeleton_website/index.md b/files/fr/learn/server-side/django/skeleton_website/index.md new file mode 100644 index 0000000000..636b371873 --- /dev/null +++ b/files/fr/learn/server-side/django/skeleton_website/index.md @@ -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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}
+ +

Ce second article de la série didactique Django 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...

+ + + + + + + + + + + + +
Prérequis:Set up a Django development environment. Avoir pris connaissance de l'article précédent.
Objectifs:Être capable d'utiliser les outils de Django pour initier un nouveau projet.
+ +

Vue d'ensemble

+ +

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.

+ +

La création est aisée:

+ +
    +
  1. Utilisez la commande django-admin pour créer le dossier du projet ainsi que les sous-dossiers et fichiers de base ainsi que le script de gestion du projet (manage.py).
  2. +
  3. Utilisez manage.py pour créer une ou plusieurs applications du projet. +
    +

    Note : 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.

    +
    +
  4. +
  5. Enregistrez la nouvelle application dans le projet.
  6. +
  7. Liez les urls et chemins pour chaque application.
  8. +
+ +

Pour le site web "Bibliothèque locale", le dossier du site web et celui du projet auront le même nom locallibrary. Une seule application catalog sera utilisée. La hiérachie de dossier du projet à la forme ci-dessous :

+ +
locallibrary/         # Website foldermanage.py         # Script to run Django tools for this project (created using django-admin)
+    locallibrary/     # Website/project folder (created using django-admin)
+    catalog/          # Application folder (created using manage.py)
+
+ +
+

Note : Afin de respecter la cohérence du code et pouvoir utiliser les développements sur GitHub, les noms du site et des applications, en anglais, n'ont pas été modifiés.

+
+ +

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.

+ +

Créer le projet locallibrary

+ +

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 environnement virtuel python, 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 django_projects et déplacez-vous dans ce dernier :

+ +
mkdir django_projects
+cd django_projects
+ +

Pour créer un nouveau projet avec le quadriciel Django, il suffit d'utiliser la commande  django-admin startproject. 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 :

+ +
django-admin startproject locallibrary
+cd locallibrary
+ +

La commande django-admin crée une arboresence contenant des fichiers et un sous-dossier portant le même nom que le projet :

+ +
locallibrary/
+    manage.py
+    locallibrary/
+        __init__.py
+        settings.py
+        urls.py
+        wsgi.py
+ +

Votre répertoire de travail est de la forme :

+ +
../django_projects/locallibrary/
+ +

Le sous-dossier locallibrary permettra de gérer les requêtes web, il contient :

+ + + +

Le fichier manage.py 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.

+ +

Créer l'application catalog

+ +

La commande ci-dessous va créer l'application catalog. Vous devez être dans le dossier de votre projet locallibrary pour exécuter cette commande (dans le même dossier que le fichier manage.py de votre projet) :

+ +
python3 manage.py startapp catalog
+ +
+

Note : La commande ci-dessus est exécutée dans un environnement Linux/macOS X. Sous Windows, il se peut que la commande soit : py -3 manage.py startapp catalog

+ +

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 python3 par py -3.

+ +

Si vous utilisez une version postérieure à la version 3.7.0, la commande devrait désormais être  py manage.py startapp catalog

+
+ +

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 views.py, le modèle de données dans models.py, etc.). Ces fichiers contiennent les éléments minimaux nécessaires à leur utilisation dans le projet.

+ +

Le dossier projet locallibrary est agrémenté d'un nouveau sous-dossier catalog :

+ +
locallibrary/
+    manage.py
+    locallibrary/
+    catalog/
+        admin.py
+        apps.py
+        models.py
+        tests.py
+        views.py
+        __init__.py
+        migrations/
+
+ +

A ceci s'ajoute :

+ + + +
+

Note : 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.

+
+ +

Enregistrer l'application catalog

+ +

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 INSTALLED_APPS en ajoutant le nom de l'application à la liste déjà présente.

+ +

Éditez le fichier django_projects/locallibrary/locallibrary/settings.py et allez jusqu'à la liste INSTALLED_APPS. Ajoutez alors comme indiqué ci-dessous l'application à la liste.

+ +
INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'catalog.apps.CatalogConfig', 
+]
+ +

Le nouvel enregistrement défini l'objet pour cette nouvelle application avec le nom (CatalogConfig) qui a été généré dans le fichier /locallibrary/catalog/apps.py quand l'application a été créée.

+ +
+

Note : Nous verrons plus loin les autres paramètres de ce fichier(comme MIDDLEWARE). Cela permet la prise en charge par Django administration site et donne accès à de nombreuses fonctionnalités (gestion des sessions, de l'authentication, etc).

+
+ +

Définir la base de données

+ +

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 bases de données prises en charge sont bien décrites sur le site du projet Django.

+ +

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 settings.py est nécessaire pour utiliser ce SGBD :

+ +
DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    }
+}
+
+ +

D'autres paramètrages du projet

+ +

Le fichier settings.py 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 (Liste des codes - en anglais). Changez la variable TIME_ZONE de votre projet avec la chaîne appropriée à votre fuseau, par exemple :

+ +
TIME_ZONE = 'Europe/Paris'
+ +

Il y a deux paramètres à connaître, même s'il ne seront pas modifiés pour l'instant :

+ + + +

Configurer le routage des URLs

+ +

La création du site web s'appuie sur un routage d'URL et une gestion de la cartographie des URLs dans le fichier urls.py) 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.

+ +

A l'ouverture du fichier locallibrary/locallibrary/urls.py,  vous pouvez remarquer les premières instructions sur la manière de gérer la cartographie des URLs.

+ +
"""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),
+]
+
+ +

Le routage des URLs est géré à l'aide de la variable urlpatterns. Elle consititue une liste Python de fonctions path(). Chaque instance path() 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 urlpatterns 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 admin.site.urls.

+ +
+

Note : Dans la fonction path(), 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 < et >, par exemple 'catalog/<id>/'). Ce motif correspondra à une URL du type /catalog/des_caracteres/. La chaîne des_caracteres sera transmis à la vue comme une chaîne de caractère associée à une variable nommée id. Ce point sera vu en détails plus loin dans la série didactique.

+
+ +

Ajoutez les lignes ci-dessous à la fin du fichier de manière à ajouter dans la variable urlpatterns une nouvelle entrée à la liste des routes. Cette nouvelle entrée permet une nouvelle route pour catalog/ dont la gestion est déléguée au fichier urls.py du module catalog (c'est-à-dire le fichier catalog/urls.py).

+ +
# 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')),
+]
+
+
+ +

Il est nécessaire de rediriger la racine du site (concrètement https://127.0.0.1:8000/) vers celui de la seule application catalog qui va être utilisée dans ce projet (concrètemen 127.0.0.1:8000/catalog/). Pour cette étape, nous utilisons la fonction particulière (RedirectView) qui prend comme argument le lien relatif (concrètement /catalog/) quand le motif de l'URL correspondra (concrètement la racine du site).

+ +

Ajoutez les lignes ci-dessous au bas du fichier urls.py :

+ +
#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)),
+]
+
+ +

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 maintenez ce mode d'écriture, vous aurez le message ci-dessous au démarrage du serveur :

+ +
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 '/'.
+
+ +

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. 

+ +

Ajoutez les lignes ci-dessous au bas du fichier urls.py :

+ +
# 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)
+
+ +
+

Note : Il y a plusieurs manière pour ajouter des routes à la variable urlpatterns (dans les étapes décrites ci-dessus nous avons ajouté petit à patir en utilisant l'opérateur += pour bien séparer les étapes). Il est en réalité tout à fait possible de tout regrouper dans une seule étape :

+ +
urlpatterns = [
+    path('admin/', admin.site.urls),
+    path('catalog/', include('catalog.urls')),
+    path('', RedirectView.as_view(url='/catalog/')),
+] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+
+ +

De même, nous avons ajouté des imports de module à chaque étapes (par exemple, from django.urls import include) 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.

+
+ +

Dernière étape ! Il faut créer le fichier urls.py dans l'application (ou le module) catalog et de définir la variable urlpatterns vide pour le moment. 

+ +
from django.urls import path
+from . import views
+
+urlpatterns = [
+
+]
+
+ +

Tester le site web

+ +

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.

+ +

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.

+ +

Exécuter la migartion de la base de données

+ +

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 locallibrary/catalog/migration) pour modifier les structures de données associées dans la base de données.

+ +

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 manage.py):

+ +
python3 manage.py makemigrations
+python3 manage.py migrate
+
+ +
+

Attention : 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).

+
+ +

L'option makemigrations 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.

+ +

L'option migrate applique les modifications sur la base de données (Django trace les modifications réalisées dans la base de données).

+ +
+

Note : Vous pouvez consulter la documentation Migrations (sur le site Django) pour plus d'informations.

+
+ +

Démarrer le site web

+ +

Pendant la phase de développement, vous pouvez tester votre serveur sur un mode local et le consulter avec votre navigateur.

+ +
+

Note : 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 http://127.0.0.1:8000/. Cependant, vous pouvez modifier ces paramètres et pour plus d'information vous pouvez consulter la documentation sur le site Django des commandes django-admin and manage.py: runserver.

+
+ +

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 manage.py) :

+ +
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.
+
+ +

Dès que le serveur est actif, vous pouvez utiliser votre navigateur est accéder à l'URL http://127.0.0.1:8000/. Vous devriez accéder à la page d'erreur ci-dessous :

+ +

Django Debug page for Django 2.0

+ +

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 catalog.urls module (que nous avons déclaré dans le fichier urls.py du projet). 

+ +
+

Note : 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é.

+
+ +

À ce stade, nous pouvons considérer que le serveur fonctionne !

+ +
+

Note : 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 !

+
+ +

Relevez le défi...

+ +

Le dossier catalog/ a été créé automatiquement et contient des fichiers pour les vues, modèles de données, etc. Ouvrez-les pour les consulter. 

+ +

Comme vous avez pu le constatez plus haut, une route pour l'administration du site (http://127.0.0.1:8000/admin/) existe déjà dans le fichier urls.py du projet. Avec votre navigateur web, vous pouvez découvrir ce qui est derrière ce site.

+ + + +

Résumé

+ +

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.

+ +

Maintenant que ceci est fait, le site web Local Library est opérationnel et nous allons passer à la partie codage et développement pour que le site produise ce qu'il est censé faire.

+ +

A voir aussi...

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}

+ +

Dans ce module

+ + diff --git a/files/fr/learn/server-side/django/testing/index.html b/files/fr/learn/server-side/django/testing/index.html deleted file mode 100644 index c61cb87e24..0000000000 --- a/files/fr/learn/server-side/django/testing/index.html +++ /dev/null @@ -1,956 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}
- -

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 tests unitaires sur votre site web en utilisant le framework de tests de Django.

- - - - - - - - - - - - -
Prérequis:Avoir terminé tous les tutoriels précédents, y compris Django Tutorial Part 9: Working with forms.
Objectif:Comprendre comment écrire des tests unitaires pour des sites web basés sur Django.
- -

Vue d'ensemble

- -

Le site Local Library 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 Book et Author, une page pour renouveler des BookInstances, et des pages pour créer, mettre à jour et effacer des éléments de type Author (et également des enregistrements de type Book, si vous avez relevé le défi dans le tutoriel sur les formulaires). Même avec ce site relativement petit, naviguer manuellement vers chaque page et vérifier superficiellement 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.

- -

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.

- -

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 test-driven et le développement behaviour-driven).

- -

Ce tutoriel montre comment écrire des tests automatisés pour Django, en ajoutant un certain nombre de tests au site web LocalLibrary.

- -

Catégories de tests

- -

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:

- -
-
Les tests unitaires
-
Ils vérifient le comportement fonctionnel de composants individuels, souvent au niveau des classes et des fonctions.
-
Les tests de régression
-
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.
-
Les test d'intégration
-
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.
-
- -
-

Note : 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.

-
- -

Que fournit Django pour tester ?

- -

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.

- -

Django fournit un framework de test avec une petite hiérarchie de classes construites sur la librairie standard de Python unittest. 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 (LiveServerTestCase) et des outils pour utiliser d'autres frameworks de test. Par exemple vous pouvez intégrer le célèbre framework Selenium pour simuler l'interaction entre un utilisateur et un vrai navigateur.

- -

Pour écrire un test, vous partez de l'une des classes de test de base fournies par Django (ou par unittest) (SimpleTestCaseTransactionTestCaseTestCaseLiveServerTestCase), 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 True ou False, 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 (setUp) et/ou un comportement de fin (tearDown) définis dans la classe, comme indiqué ci-dessous.

- -
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)
-
- -

La meilleure classe de base pour la plupart des tests est django.test.TestCase. 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 Client 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 TestCase.

- -
-

Note : La classe django.test.TestCase 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.

-
- -

Que faut-il tester ?

- -

Vous pouvez tester tous les aspects de votre code, mais non les librairies ou fonctionnalités faisant partie de Python ou de Django.

- -

Ainsi par exemple, considérez le modèle Author défini ci-dessous. Vous n'avez pas besoin de tester explicitement que first_name et last_name ont été stockés correctement comme CharField 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 date_of_birth a été validé comme champ date, car, encore une fois, cela est implémenté par Django.

- -

En revanche, vous pouvez tester que les textes utilisés pour les labels (First name, Last name, Date of birth, Died), 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.

- -
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)
- -

De même, vous pouvez tester que les méthodes personnalisées get_absolute_url() et __str__() se comportent comme prévu, car elles appartiennent à votre logique code/métier. Dans le cas de get_absolute_url(), vous pouvez supposer que la méthode Django reverse() a été implémentée correctement, aussi ce que vous allez tester, c'est que la vue associée a été effectivement définie.

- -
-

Note : 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.)

-
- -

Avec cela en tête, commençons à voir comment définir et lancer des tests.

- -

Vue d'ensemble de la structure de test

- -

Avant d'entrer dans le détail de "que tester", voyons d'abord brièvement et comment les tests sont définis.

- -

Django utilise le built-in test discovery 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 test*.py. 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 :

- -
catalog/
-  /tests/
-    __init__.py
-    test_models.py
-    test_forms.py
-    test_views.py
-
- -

Créez une structure de fichier comme montré ci-dessus, dans votre projet LocalLibrary. Le ficheir __init__.py 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 /catalog/tests.py.

- -
-

Note : le fichier de test du squelette /catalog/tests.py a été créé automatiquement quand nous avons construit le squelette du site web Django. 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.

- -

Supprimez le fichier de squelette, car nous n'en aurons plus besoin.

-
- -

Ouvrez le fichier /catalog/tests/test_models.py. Ce fichier doit importer django.test.TestCase, comme indiqué ci-après :

- -

Open /catalog/tests/test_models.py. The file should import django.test.TestCase, as shown:

- -
from django.test import TestCase
-
-# Create your tests here.
-
- -

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.

- -

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 TestCase.

- -
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)
- -

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) :

- - - -
-

Note : Les classes de test ont aussi une méthode tearDown(), 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 TestCase prend soin pour vous de supprimer la base de données utilisées pour les tests.

-
- -

En dessous de ces méthodes, nous avons un certain nombre de méthodes de test, qui utilisent des fonctions Assert, pour tester si certaines conditions sont vraies, fausses ou égales (AssertTrue, AssertFalse, AssertEqual). Si la condition ne renvoie pas le résultat escompté, le test plante et renvoie une erreur à votre console.

- -

Les méthodes AssertTrue, AssertFalse et AssertEqual sont des assertions standard fournies par unittest. Il y a d'autres assertions standard dans le framework, et aussi des assertions spécifiques à Django, pour tester si une vue redirige (assertRedirects), pour tester si tel template a été utilisé (assertTemplateUsed), etc.

- -
-

Note : Normallement vous ne devriez pas inclure de fonctions print() 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.

-
- -

Comment lancer les tests

- -

La manière la plus facile pour lancer tous les tests est d'utiliser la commande :

- -
python3 manage.py test
- -

Cette commande va lancer la recherche de tous les fichiers ayant la forme test*.py 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 /catalog/tests/test_models.py contient des tests). Par défaut, chaque test ne fera de rapport qu'en cas d'échec, avec ensuite un résumé du test.

- -
-

Note : Si vous obtenez des erreurs telles que : ValueError: Missing staticfiles manifest entry ..., cela peut être dû au fait que le test ne lance pas collectstatic par défaut, et que votre application utilise une classe de storage qui le requiert (voyez manifest_strict pour plus d'information). Il y a plusieurs façons de remédier à ce problème - la plus facile est de lancer tout simplement collectstatic avant de lancer les tests :

- -
python3 manage.py collectstatic
-
-
- -

Lancez les tests dans le répertoire racine de LocalLibrary. Vous devriez voir une sortie semblable à celle ci-dessous.

- -
> python3 manage.py test
-
-Creating test database for alias 'default'...
-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.
-.
-======================================================================
-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'...
- -

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 False n'est pas True !).

- -
-

Note : 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.

-
- -

Le texte en gras ci-dessus n'apparaîtra pas normalement dans la sortie de test (elle est générée par les fonctions print() dans nos tests). Cela montre comment la méthode setUpTestData() est appelée une fois pour l'ensemble de classe, tandis que setUp() est appelée avant chaque méthode.

- -

La section suivante mnotre comment vous pouvez lancer des test spécifiques, et comment contrôler la quantité d'information fournie par les tests.

- -

Montrer plus d'informations à propos du test

- -

Si vous souhaitez obtenir plus d'informations à propos du test lancé, vous pouvez changer sa verbosité. 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 :

- -
python3 manage.py test --verbosity 2
- -

Les niveaux de verbosité sont 0, 1, 2 et 3, avec "1" comme valeur par défaut.

- -

Lancer des tests spécifiques

- -

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 TestCase ou méthode :

- -
# 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
-
- -

Tests de LocalLibrary

- -

Maintenant que nous savons comment lancer nos tests et quel genre de choses nous avons besoin de tester, regardons quelques exemples pratiques.

- -
-

Note : 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.

-
- -

Modèles

- -

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.

- -

Par exemple, considérez le modèle Author 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.

- -
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}'
- -

Ouvrez notre /catalog/tests/test_models.py, et remplacez tout le code qui s'y trouve par le code de test ci-après pour le modèle Author.

- -

Vous voyez que nous importons d'abord TestCase et faisons dériver d'elle notre classe de test (AuthorModelTest) en utilisant un nom descriptif, de façon à pouvoir identifier aisément dans la sortie tout test qui échoue. Nous appelons ensuite setUpTestData() afin de créer un objet author, que nous utiliserons mais que nous ne modifierons jamais dans aucun de nos tests.

- -
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')
- -

Les tests de champ vérifient que les valeurs des labels de champ (verbose_name) et que la taille des champs de type character sont tels que nous les souhaitons. Ces méthodes ont toutes des noms descriptifs et suivent le même pattern :

- -
# 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')
- -

Les choses intéressantes à noter sont :

- - - -
-

Note : Les tests pour les labels de last_name et date_of_birth, ainsi que le test de la longueur du champ last_name, 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.

-
- -

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 Author est telle que nous l'attendons.

- -
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')
- -

Maintenant lancez les tests. Si vous avez créé le modèle Author comme décrit dans le tutoriel sur les modèles, il est assez probable que vous allez obtenir une erreur pour le label date_of_death, 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).

- -
======================================================================
-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
-? ^
- -

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.

- -
-

Note : Changez en "died" le label pour le champ date_of_death (/catalog/models.py) et relancez les tests.

-
- -

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.

- -

Les Formulaires

- -

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.

- -

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.

- -

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.

- -
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 < datetime.date.today():
-            raise ValidationError(_('Invalid date - renewal in past'))
-
-        # Check if date is in the allowed range (+4 weeks from today).
-        if data > datetime.date.today() + datetime.timedelta(weeks=4):
-            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
-
-        # Remember to always return the cleaned data.
-        return data
- -

Ouvrez notre fichier /catalog/tests/test_forms.py, et remplacez tout le code qui s'y trouve par le code suivant, qui teste le formulaire RenewBookForm. 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 TestCase.

- -
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())
-
- -

Les deux premières fonctions testent que le label et le help_text du champ sont tels qu'on les attend. Nous devons accéder au champ en utilisant le dictionnaire du champ (p. ex. form.fields['renewal_date']). Notez bien ici que nous devons aussi tester si la valeur du label est None, car même si Django rend le label correct, il retournera None si la valeur n'est pas définie explicitement.

- -

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 (datetime.date.today()) en utilisant datetime.timedelta() (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.

- -
-

Note : Ici nous n'utilisons pas réellement la base de données ni le client de test. Envisagez de modifier ces tests pour utiliser SimpleTestCase.

- -

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.

-
- -

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 !

- -

Vues

- -

Pour valider le comportement de notre vue, nous utilisons le client de test de Django. Cette classe se comporte comme un navigateur web fictif que nous pouvons utiliser pour simuler des requêtes GET et POST à 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.

- -

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 /catalog/authors/ (une URL nommée 'authors' dans la configuration des URL).

- -
class AuthorListView(generic.ListView):
-    model = Author
-    paginate_by = 10
-
- -

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.

- -

Ouvrez le fichier /catalog/tests/test_views.py, et remplacez tout texte existant par le code de test suivant, pour la vue AuthorListView. Comme auparavant, nous importons notre modèle et quelques classes utiles. Dans la méthode setUpTestData(), nous définissons un certain nombre d'objets Author, de façon à pouvoir tester notre pagination.

- -
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)
- -

Tous les tests utilisent le client (qui appartient à notre classe dérivée de TestCase), afin de simuler une requête GET 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.

- -
response = self.client.get('/catalog/authors/')
-response = self.client.get(reverse('authors'))
-
- -

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.

- -
-

Note : Si, dans votre fichier /catalog/views.py, vous mettez la variable paginate_by à 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 :

- -
self.assertTrue(len(response.context['author_list']) == 5)
-
-
- -

La variable la plus intéressante que nous montrons ci-dessus est response.context, 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.

- -

Vues limitées aux utilisateurs connectés

- -

Dans certains cas, vous voudrez tester une vue qui est limitée aux seuls utilisateurs connectés. Par exemple notre vue LoanedBooksByUserListView 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 BookInstance qui sont empruntés par l'utilisateur courant, ont le statut 'on loan', et sont triés 'le plus ancien en premier'.

- -
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')
- -

Ajoutez le code de test suivant à /catalog/tests/test_views.py. Ici nous utilisons d'abord la méthode SetUp() pour créer des logins de comptes d'utilisateurs et des objets de type BookInstance (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é SetUp() plutôt que setUpTestData(), parce que nous allons modifier plus tard certains de ces objets.

- -
-

Note : Le code de setUp() ci-dessous crée un livre avec un Language spécifique, mais votre code n'inclut peut-être pas le modèle Language, étant donné que celui-ci a été créé lors d'un défi. 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 RenewBookInstancesViewTest qui suit.

-
- -
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<ISRUkw+tuK')
-        test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&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<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')
-
- -

Pour vérifier que la vue redirige à une page de login si l'utilisateur n'est pas connecté, nous utilisons assertRedirects, comme montré dans test_redirect_if_not_logged_in(). 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 status_code de 200 (succès).

- -

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.

- -
    def test_only_borrowed_books_in_list(self):
-        login = self.client.login(username='testuser1', password='1X<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<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 <= book.due_back)
-                last_date = book.due_back
- -

Vous pourriez aussi ajouter les tests de pagination, si vous voulez !

- -

Tester des vues avec formulaires

- -

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.

- -

Pour voir cela, écrivons des tests pour la vue utilisée pour renouveler des livres (renew_book_librarian()) :

- -
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)
- -

Nous allons devoir tester que la vue n'est disponible qu'aux utilisateurs ayant la permission can_mark_returned, et que les utilisateurs sont bien redirigés vers une page d'erreur HTTP 404 s'ils essaient de renouveler une BookInstance 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.

- -

Ajoutez la première partie de la classe de test ci-dessous à la fin du fichier /catalog/tests/test_views.py. 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 :

- -
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<ISRUkw+tuK')
-        test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&3iD')
-
-        test_user1.save()
-        test_user2.save()
-
-        permission = Permission.objects.get(name='Set book as returned')
-        test_user2.user_permissions.add(permission)
-        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 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',
-        )
- -

Ajoutez les tests suivants à la fin de la classe de test. Ils vérifient que seuls les utilisateurs avec les bonnes permissions (testuser2) 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 BookInstance inexistante. Nous vérifions aussi que le bon template est utilisé.

- -
   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<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&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&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&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&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')
-
- -

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).

- -
    def test_form_renewal_date_initially_has_date_three_weeks_in_future(self):
-        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&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.context['form'].initial['renewal_date'], date_3_weeks_in_future)
-
- -
-

Attention : Si vous utilisez la class de formulaire RenewBookModelForm(forms.ModelForm) à la place de la classe RenewBookForm(forms.Form), le nom du champ est 'due_back' et non 'renewal_date'.

-
- -

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 POSTer 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.

- -
    def test_redirects_to_all_borrowed_book_list_on_success(self):
-        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&3iD')
-        valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2)
-        response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future})
-        self.assertRedirects(response, reverse('all-borrowed'))
-
- -
-

Attention : La vue all-borrowed a été ajoutée comme défi, 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 follow=True dans la requête s'assure que la requête retourne l'URL de la destination finale (donc vérifie /catalog/ plutôt que /).

- -
 response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future}, follow=True )
- self.assertRedirects(response, '/catalog/')
-
- -

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.

- -
    def test_form_invalid_renewal_date_past(self):
-        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&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)
-        self.assertFormError(response, 'form', 'renewal_date', 'Invalid date - renewal in past')
-
-    def test_form_invalid_renewal_date_future(self):
-        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&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)
-        self.assertFormError(response, 'form', 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead')
-
- -

Le même genre de technique peut être utilisé pour tester les autres vues.

- -

Templates

- -

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é.

- -

Autres outils de test recommandés

- -

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).

- -

Comme il y a un grand nombre d'autres outils de test à votre disposition, nous ne mentionnerons que les deux suivants :

- - - -

Défi

- -

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 AuthorCreate.

- -
class AuthorCreate(PermissionRequiredMixin, CreateView):
-    model = Author
-    fields = '__all__'
-    initial = {'date_of_death':'12/10/2016'}
-    permission_required = 'catalog.can_mark_returned'
- -

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.

- -

Résumé

- -

É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.

- -

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.

- -

Le prochain (et dernier) tutoriel montre comment vous pouvez déployer votre merveilleux (et entièrement testé !) site web Django.

- -

À voir également

- - - -

{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/server-side/django/testing/index.md b/files/fr/learn/server-side/django/testing/index.md new file mode 100644 index 0000000000..c61cb87e24 --- /dev/null +++ b/files/fr/learn/server-side/django/testing/index.md @@ -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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}
+ +

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 tests unitaires sur votre site web en utilisant le framework de tests de Django.

+ + + + + + + + + + + + +
Prérequis:Avoir terminé tous les tutoriels précédents, y compris Django Tutorial Part 9: Working with forms.
Objectif:Comprendre comment écrire des tests unitaires pour des sites web basés sur Django.
+ +

Vue d'ensemble

+ +

Le site Local Library 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 Book et Author, une page pour renouveler des BookInstances, et des pages pour créer, mettre à jour et effacer des éléments de type Author (et également des enregistrements de type Book, si vous avez relevé le défi dans le tutoriel sur les formulaires). Même avec ce site relativement petit, naviguer manuellement vers chaque page et vérifier superficiellement 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.

+ +

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.

+ +

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 test-driven et le développement behaviour-driven).

+ +

Ce tutoriel montre comment écrire des tests automatisés pour Django, en ajoutant un certain nombre de tests au site web LocalLibrary.

+ +

Catégories de tests

+ +

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:

+ +
+
Les tests unitaires
+
Ils vérifient le comportement fonctionnel de composants individuels, souvent au niveau des classes et des fonctions.
+
Les tests de régression
+
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.
+
Les test d'intégration
+
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.
+
+ +
+

Note : 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.

+
+ +

Que fournit Django pour tester ?

+ +

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.

+ +

Django fournit un framework de test avec une petite hiérarchie de classes construites sur la librairie standard de Python unittest. 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 (LiveServerTestCase) et des outils pour utiliser d'autres frameworks de test. Par exemple vous pouvez intégrer le célèbre framework Selenium pour simuler l'interaction entre un utilisateur et un vrai navigateur.

+ +

Pour écrire un test, vous partez de l'une des classes de test de base fournies par Django (ou par unittest) (SimpleTestCaseTransactionTestCaseTestCaseLiveServerTestCase), 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 True ou False, 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 (setUp) et/ou un comportement de fin (tearDown) définis dans la classe, comme indiqué ci-dessous.

+ +
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)
+
+ +

La meilleure classe de base pour la plupart des tests est django.test.TestCase. 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 Client 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 TestCase.

+ +
+

Note : La classe django.test.TestCase 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.

+
+ +

Que faut-il tester ?

+ +

Vous pouvez tester tous les aspects de votre code, mais non les librairies ou fonctionnalités faisant partie de Python ou de Django.

+ +

Ainsi par exemple, considérez le modèle Author défini ci-dessous. Vous n'avez pas besoin de tester explicitement que first_name et last_name ont été stockés correctement comme CharField 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 date_of_birth a été validé comme champ date, car, encore une fois, cela est implémenté par Django.

+ +

En revanche, vous pouvez tester que les textes utilisés pour les labels (First name, Last name, Date of birth, Died), 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.

+ +
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)
+ +

De même, vous pouvez tester que les méthodes personnalisées get_absolute_url() et __str__() se comportent comme prévu, car elles appartiennent à votre logique code/métier. Dans le cas de get_absolute_url(), vous pouvez supposer que la méthode Django reverse() a été implémentée correctement, aussi ce que vous allez tester, c'est que la vue associée a été effectivement définie.

+ +
+

Note : 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.)

+
+ +

Avec cela en tête, commençons à voir comment définir et lancer des tests.

+ +

Vue d'ensemble de la structure de test

+ +

Avant d'entrer dans le détail de "que tester", voyons d'abord brièvement et comment les tests sont définis.

+ +

Django utilise le built-in test discovery 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 test*.py. 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 :

+ +
catalog/
+  /tests/
+    __init__.py
+    test_models.py
+    test_forms.py
+    test_views.py
+
+ +

Créez une structure de fichier comme montré ci-dessus, dans votre projet LocalLibrary. Le ficheir __init__.py 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 /catalog/tests.py.

+ +
+

Note : le fichier de test du squelette /catalog/tests.py a été créé automatiquement quand nous avons construit le squelette du site web Django. 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.

+ +

Supprimez le fichier de squelette, car nous n'en aurons plus besoin.

+
+ +

Ouvrez le fichier /catalog/tests/test_models.py. Ce fichier doit importer django.test.TestCase, comme indiqué ci-après :

+ +

Open /catalog/tests/test_models.py. The file should import django.test.TestCase, as shown:

+ +
from django.test import TestCase
+
+# Create your tests here.
+
+ +

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.

+ +

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 TestCase.

+ +
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)
+ +

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) :

+ + + +
+

Note : Les classes de test ont aussi une méthode tearDown(), 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 TestCase prend soin pour vous de supprimer la base de données utilisées pour les tests.

+
+ +

En dessous de ces méthodes, nous avons un certain nombre de méthodes de test, qui utilisent des fonctions Assert, pour tester si certaines conditions sont vraies, fausses ou égales (AssertTrue, AssertFalse, AssertEqual). Si la condition ne renvoie pas le résultat escompté, le test plante et renvoie une erreur à votre console.

+ +

Les méthodes AssertTrue, AssertFalse et AssertEqual sont des assertions standard fournies par unittest. Il y a d'autres assertions standard dans le framework, et aussi des assertions spécifiques à Django, pour tester si une vue redirige (assertRedirects), pour tester si tel template a été utilisé (assertTemplateUsed), etc.

+ +
+

Note : Normallement vous ne devriez pas inclure de fonctions print() 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.

+
+ +

Comment lancer les tests

+ +

La manière la plus facile pour lancer tous les tests est d'utiliser la commande :

+ +
python3 manage.py test
+ +

Cette commande va lancer la recherche de tous les fichiers ayant la forme test*.py 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 /catalog/tests/test_models.py contient des tests). Par défaut, chaque test ne fera de rapport qu'en cas d'échec, avec ensuite un résumé du test.

+ +
+

Note : Si vous obtenez des erreurs telles que : ValueError: Missing staticfiles manifest entry ..., cela peut être dû au fait que le test ne lance pas collectstatic par défaut, et que votre application utilise une classe de storage qui le requiert (voyez manifest_strict pour plus d'information). Il y a plusieurs façons de remédier à ce problème - la plus facile est de lancer tout simplement collectstatic avant de lancer les tests :

+ +
python3 manage.py collectstatic
+
+
+ +

Lancez les tests dans le répertoire racine de LocalLibrary. Vous devriez voir une sortie semblable à celle ci-dessous.

+ +
> python3 manage.py test
+
+Creating test database for alias 'default'...
+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.
+.
+======================================================================
+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'...
+ +

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 False n'est pas True !).

+ +
+

Note : 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.

+
+ +

Le texte en gras ci-dessus n'apparaîtra pas normalement dans la sortie de test (elle est générée par les fonctions print() dans nos tests). Cela montre comment la méthode setUpTestData() est appelée une fois pour l'ensemble de classe, tandis que setUp() est appelée avant chaque méthode.

+ +

La section suivante mnotre comment vous pouvez lancer des test spécifiques, et comment contrôler la quantité d'information fournie par les tests.

+ +

Montrer plus d'informations à propos du test

+ +

Si vous souhaitez obtenir plus d'informations à propos du test lancé, vous pouvez changer sa verbosité. 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 :

+ +
python3 manage.py test --verbosity 2
+ +

Les niveaux de verbosité sont 0, 1, 2 et 3, avec "1" comme valeur par défaut.

+ +

Lancer des tests spécifiques

+ +

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 TestCase ou méthode :

+ +
# 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
+
+ +

Tests de LocalLibrary

+ +

Maintenant que nous savons comment lancer nos tests et quel genre de choses nous avons besoin de tester, regardons quelques exemples pratiques.

+ +
+

Note : 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.

+
+ +

Modèles

+ +

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.

+ +

Par exemple, considérez le modèle Author 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.

+ +
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}'
+ +

Ouvrez notre /catalog/tests/test_models.py, et remplacez tout le code qui s'y trouve par le code de test ci-après pour le modèle Author.

+ +

Vous voyez que nous importons d'abord TestCase et faisons dériver d'elle notre classe de test (AuthorModelTest) en utilisant un nom descriptif, de façon à pouvoir identifier aisément dans la sortie tout test qui échoue. Nous appelons ensuite setUpTestData() afin de créer un objet author, que nous utiliserons mais que nous ne modifierons jamais dans aucun de nos tests.

+ +
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')
+ +

Les tests de champ vérifient que les valeurs des labels de champ (verbose_name) et que la taille des champs de type character sont tels que nous les souhaitons. Ces méthodes ont toutes des noms descriptifs et suivent le même pattern :

+ +
# 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')
+ +

Les choses intéressantes à noter sont :

+ + + +
+

Note : Les tests pour les labels de last_name et date_of_birth, ainsi que le test de la longueur du champ last_name, 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.

+
+ +

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 Author est telle que nous l'attendons.

+ +
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')
+ +

Maintenant lancez les tests. Si vous avez créé le modèle Author comme décrit dans le tutoriel sur les modèles, il est assez probable que vous allez obtenir une erreur pour le label date_of_death, 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).

+ +
======================================================================
+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
+? ^
+ +

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.

+ +
+

Note : Changez en "died" le label pour le champ date_of_death (/catalog/models.py) et relancez les tests.

+
+ +

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.

+ +

Les Formulaires

+ +

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.

+ +

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.

+ +

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.

+ +
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 < datetime.date.today():
+            raise ValidationError(_('Invalid date - renewal in past'))
+
+        # Check if date is in the allowed range (+4 weeks from today).
+        if data > datetime.date.today() + datetime.timedelta(weeks=4):
+            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+        # Remember to always return the cleaned data.
+        return data
+ +

Ouvrez notre fichier /catalog/tests/test_forms.py, et remplacez tout le code qui s'y trouve par le code suivant, qui teste le formulaire RenewBookForm. 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 TestCase.

+ +
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())
+
+ +

Les deux premières fonctions testent que le label et le help_text du champ sont tels qu'on les attend. Nous devons accéder au champ en utilisant le dictionnaire du champ (p. ex. form.fields['renewal_date']). Notez bien ici que nous devons aussi tester si la valeur du label est None, car même si Django rend le label correct, il retournera None si la valeur n'est pas définie explicitement.

+ +

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 (datetime.date.today()) en utilisant datetime.timedelta() (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.

+ +
+

Note : Ici nous n'utilisons pas réellement la base de données ni le client de test. Envisagez de modifier ces tests pour utiliser SimpleTestCase.

+ +

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.

+
+ +

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 !

+ +

Vues

+ +

Pour valider le comportement de notre vue, nous utilisons le client de test de Django. Cette classe se comporte comme un navigateur web fictif que nous pouvons utiliser pour simuler des requêtes GET et POST à 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.

+ +

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 /catalog/authors/ (une URL nommée 'authors' dans la configuration des URL).

+ +
class AuthorListView(generic.ListView):
+    model = Author
+    paginate_by = 10
+
+ +

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.

+ +

Ouvrez le fichier /catalog/tests/test_views.py, et remplacez tout texte existant par le code de test suivant, pour la vue AuthorListView. Comme auparavant, nous importons notre modèle et quelques classes utiles. Dans la méthode setUpTestData(), nous définissons un certain nombre d'objets Author, de façon à pouvoir tester notre pagination.

+ +
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)
+ +

Tous les tests utilisent le client (qui appartient à notre classe dérivée de TestCase), afin de simuler une requête GET 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.

+ +
response = self.client.get('/catalog/authors/')
+response = self.client.get(reverse('authors'))
+
+ +

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.

+ +
+

Note : Si, dans votre fichier /catalog/views.py, vous mettez la variable paginate_by à 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 :

+ +
self.assertTrue(len(response.context['author_list']) == 5)
+
+
+ +

La variable la plus intéressante que nous montrons ci-dessus est response.context, 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.

+ +

Vues limitées aux utilisateurs connectés

+ +

Dans certains cas, vous voudrez tester une vue qui est limitée aux seuls utilisateurs connectés. Par exemple notre vue LoanedBooksByUserListView 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 BookInstance qui sont empruntés par l'utilisateur courant, ont le statut 'on loan', et sont triés 'le plus ancien en premier'.

+ +
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')
+ +

Ajoutez le code de test suivant à /catalog/tests/test_views.py. Ici nous utilisons d'abord la méthode SetUp() pour créer des logins de comptes d'utilisateurs et des objets de type BookInstance (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é SetUp() plutôt que setUpTestData(), parce que nous allons modifier plus tard certains de ces objets.

+ +
+

Note : Le code de setUp() ci-dessous crée un livre avec un Language spécifique, mais votre code n'inclut peut-être pas le modèle Language, étant donné que celui-ci a été créé lors d'un défi. 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 RenewBookInstancesViewTest qui suit.

+
+ +
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<ISRUkw+tuK')
+        test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&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<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')
+
+ +

Pour vérifier que la vue redirige à une page de login si l'utilisateur n'est pas connecté, nous utilisons assertRedirects, comme montré dans test_redirect_if_not_logged_in(). 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 status_code de 200 (succès).

+ +

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.

+ +
    def test_only_borrowed_books_in_list(self):
+        login = self.client.login(username='testuser1', password='1X<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<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 <= book.due_back)
+                last_date = book.due_back
+ +

Vous pourriez aussi ajouter les tests de pagination, si vous voulez !

+ +

Tester des vues avec formulaires

+ +

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.

+ +

Pour voir cela, écrivons des tests pour la vue utilisée pour renouveler des livres (renew_book_librarian()) :

+ +
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)
+ +

Nous allons devoir tester que la vue n'est disponible qu'aux utilisateurs ayant la permission can_mark_returned, et que les utilisateurs sont bien redirigés vers une page d'erreur HTTP 404 s'ils essaient de renouveler une BookInstance 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.

+ +

Ajoutez la première partie de la classe de test ci-dessous à la fin du fichier /catalog/tests/test_views.py. 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 :

+ +
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<ISRUkw+tuK')
+        test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&3iD')
+
+        test_user1.save()
+        test_user2.save()
+
+        permission = Permission.objects.get(name='Set book as returned')
+        test_user2.user_permissions.add(permission)
+        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 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',
+        )
+ +

Ajoutez les tests suivants à la fin de la classe de test. Ils vérifient que seuls les utilisateurs avec les bonnes permissions (testuser2) 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 BookInstance inexistante. Nous vérifions aussi que le bon template est utilisé.

+ +
   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<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&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&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&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&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')
+
+ +

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).

+ +
    def test_form_renewal_date_initially_has_date_three_weeks_in_future(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&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.context['form'].initial['renewal_date'], date_3_weeks_in_future)
+
+ +
+

Attention : Si vous utilisez la class de formulaire RenewBookModelForm(forms.ModelForm) à la place de la classe RenewBookForm(forms.Form), le nom du champ est 'due_back' et non 'renewal_date'.

+
+ +

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 POSTer 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.

+ +
    def test_redirects_to_all_borrowed_book_list_on_success(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&3iD')
+        valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2)
+        response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future})
+        self.assertRedirects(response, reverse('all-borrowed'))
+
+ +
+

Attention : La vue all-borrowed a été ajoutée comme défi, 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 follow=True dans la requête s'assure que la requête retourne l'URL de la destination finale (donc vérifie /catalog/ plutôt que /).

+ +
 response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future}, follow=True )
+ self.assertRedirects(response, '/catalog/')
+
+ +

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.

+ +
    def test_form_invalid_renewal_date_past(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&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)
+        self.assertFormError(response, 'form', 'renewal_date', 'Invalid date - renewal in past')
+
+    def test_form_invalid_renewal_date_future(self):
+        login = self.client.login(username='testuser2', password='2HJ1vRV0Z&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)
+        self.assertFormError(response, 'form', 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead')
+
+ +

Le même genre de technique peut être utilisé pour tester les autres vues.

+ +

Templates

+ +

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é.

+ +

Autres outils de test recommandés

+ +

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).

+ +

Comme il y a un grand nombre d'autres outils de test à votre disposition, nous ne mentionnerons que les deux suivants :

+ + + +

Défi

+ +

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 AuthorCreate.

+ +
class AuthorCreate(PermissionRequiredMixin, CreateView):
+    model = Author
+    fields = '__all__'
+    initial = {'date_of_death':'12/10/2016'}
+    permission_required = 'catalog.can_mark_returned'
+ +

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.

+ +

Résumé

+ +

É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.

+ +

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.

+ +

Le prochain (et dernier) tutoriel montre comment vous pouvez déployer votre merveilleux (et entièrement testé !) site web Django.

+ +

À voir également

+ + + +

{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}

+ +

Dans ce module

+ + 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 deleted file mode 100644 index 7e085e43b8..0000000000 --- a/files/fr/learn/server-side/django/tutorial_local_library_website/index.html +++ /dev/null @@ -1,101 +0,0 @@ ---- -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 ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}
- -

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.

- -
- - - - - - - - - - - - -
Prérequis:La lecture de l'introduction. Pour les articles suivants avoir mis à jour l'environnement comme décrit précédemment. 
Objectifs:Présenter l'application à venir et les thèmes qui seront abordés dans cette série didactique.
- -

Vue d'ensemble

- -

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.

- -

Dans les articles qui suivent, vous allez apprendre à :

- - - -

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.

- -

Le site web de la "Bibliothèque locale"

- -

La LocalLibrary (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.

- -

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 :

- - - -

Même s'il s'agit d'un sujet extensible, son sujet de Bibliothèque locale 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 grande biblothèque

- -

Je suis coincé, où puis-je trouver les sources ?

- -

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.

- -

Si vous êtes coincé, vous pourrez trouver une version totalement développée du site sur Github (Anglais).

- -

Résumé

- -

Vous en savez plus sur le projet LocalLibrary et ce que vous allez progressivement apprendre, il est désormais temps de créer le squellette du projet qui hébergera la bibliothèque.

- -

{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}

- - - -

Dans ce module

- - diff --git a/files/fr/learn/server-side/django/tutorial_local_library_website/index.md b/files/fr/learn/server-side/django/tutorial_local_library_website/index.md new file mode 100644 index 0000000000..7e085e43b8 --- /dev/null +++ b/files/fr/learn/server-side/django/tutorial_local_library_website/index.md @@ -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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}
+ +

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.

+ +
+ + + + + + + + + + + + +
Prérequis:La lecture de l'introduction. Pour les articles suivants avoir mis à jour l'environnement comme décrit précédemment. 
Objectifs:Présenter l'application à venir et les thèmes qui seront abordés dans cette série didactique.
+ +

Vue d'ensemble

+ +

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.

+ +

Dans les articles qui suivent, vous allez apprendre à :

+ + + +

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.

+ +

Le site web de la "Bibliothèque locale"

+ +

La LocalLibrary (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.

+ +

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 :

+ + + +

Même s'il s'agit d'un sujet extensible, son sujet de Bibliothèque locale 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 grande biblothèque

+ +

Je suis coincé, où puis-je trouver les sources ?

+ +

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.

+ +

Si vous êtes coincé, vous pourrez trouver une version totalement développée du site sur Github (Anglais).

+ +

Résumé

+ +

Vous en savez plus sur le projet LocalLibrary et ce que vous allez progressivement apprendre, il est désormais temps de créer le squellette du projet qui hébergera la bibliothèque.

+ +

{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}

+ + + +

Dans ce module

+ + diff --git a/files/fr/learn/server-side/express_nodejs/index.html b/files/fr/learn/server-side/express_nodejs/index.html deleted file mode 100644 index abd2352b57..0000000000 --- a/files/fr/learn/server-side/express_nodejs/index.html +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Express Web Framework (Node.js/JavaScript) -slug: Learn/Server-side/Express_Nodejs -tags: - - Beginner - - CodingScripting - - Express - - Express.js - - Intro - - JavaScript - - Learn - - Node - - Server-side programming - - node.js -translation_of: Learn/Server-side/Express_Nodejs ---- -
{{LearnSidebar}}
- -

Express est une infrastructure d'application (framework), écrit en JavaScript et hébergée dans l'environnement d'exécution node.js. Cette section explique certains de ses principaux avantages, comment configurer votre environnement de développement et comment effectuer des tâches courantes de développement et de déploiement.

- -

Prérequis

- -

Avant d'aller plus loin, vous devrez avoir compris ce qu'est la programmation côté serveur et le concept de "framework", idéalement en ayant lu notre article Premiers pas en programmation côté-serveur. Une connaissance générale des concepts de programmation et du JavaScript est vivement recommandée, sans pour autant être essentielle à la compréhension des concepts fondamentaux.

- -
-

Note : Le site MDN possède de nombreuses ressources utiles pour apprendre JavaScript dans le contexte du développement côté client : Référence JavaScript, Guide JavaScript, Les bases de JavaScript, Apprendre JavaScript. Le noyau du langage et les principaux concepts de JavaScript sont les mêmes que pour le développement côté serveur sur Node.js, il est donc important d'avoir déjà une bonne compréhension de JavaScript avant de continuer. Node.js offre des API supplémentaires pour supporter des fonctionnalités utiles dans des environnements sans navigateur, par ex. pour créer des serveurs HTTP et accéder au système de fichiers, mais ne prend pas en charge les API JavaScript pour travailler avec le navigateur et le DOM.

- -

Ce guide vous fournira des informations sur l'utilisation de Node.js et Express, et contient de nombreuses références à d'autres excellentes ressources sur Internet et dans des livres — dont des liens depuis How do I get started with Node.js (StackOverflow) et What are the best resources for learning Node.js? (Quora).

-
- -

Guides

- -
-
Introduction à Express/Node
-
Dans ce premier article sur Express nous répondons aux questions « Qu'est-ce que Node ? » et « Qu'est-ce qu'Express ? » et donnerons une vue d'ensemble de ce qui rend cette infrastructure d'application Express si spéciale. Nous décrirons les principales fonctionnalités et montrerons quelques-uns des principaux éléments constitutifs d'une application Express (bien qu'à ce stade, vous n'aurez pas encore un environnement de développement dans lequel la tester)
-
Installer un environnement de développement pour Node (Express)
-
Maintenant que nous savons à quoi sert Express, nous allons vous apprendre à installer et tester un environnement de développement Node/Express sur Windows, Linux (Ubuntu), et macOS. Quel que soit votre système d'exploitation, cet article devrait vous fournir le nécessaire pour commencer le développement d'applications Express.
-
Tutoriel Express : le site d'une bibliothèque locale
-
Le premier article de notre série de tutoriels explique ce que vous allez apprendre et fournit l'aperçu d'un exemple de site pour une bibliothèque municipale locale. Nous ferons évoluer cet exemple au fur et à mesure des articles suivants.
-
2e partie du tutoriel Express : création d'un squelette de site web
-
Cet article explique comment vous pouvez créer un « squelette » de projet de site web, que vous pouvez alors étoffer de routes spécifiques, de modèles de vues et de bases de données.
-
3e partie du tutoriel Express : utilisation d'une base de données (avec Mongoose)
-
Cet article introduit brièvement le concept de base de données pour Node/Express. Il se poursuit en décrivant comment on peut utiliser Mongoose pour fournir un accès à la base de données de notre site internet de bibliothèque locale. Il explique comment les schémas de données et les modèles sont déclarées, les types principaux des champs de données, et les validations de base. Il donne aussi un aperçu rapide de quelques moyens d'accéder aux données.
-
4e partie du tutoriel Express : les routes et les contrôleurs
-
Dans ce tutoriel nous programmerons des routes (traitements des URL) avec des fonctions factices de traitement pour tous les points de sortie dont nous pourrions éventuellement avoir besoin pour notre site web de la bibliothèque locale. Nous obtiendrons ainsi une structure modulaire pour notre code de traitement par les routes, que nous pourrons étendre avec de vraies fonctions de traitement dans les articles suivants. Nous acquerrons de cette manière une réelle compréhension de la création modulaire des routes avec Express.
-
5e partie du tutoriel Express : affichage des données de la bibliothèque
-
Nous sommes prêts maintenant à ajouter des pages qui afficheront les livres et autres données de notre bibliothèque locale sur son site internet. Ces pages incluront notamment une page d'accueil qui indiquera le nombre d'enregistrements pour chaque type de modèle de donnée, une liste et des pages de détail pour chacun de ces modèles. Chemin faisant, nous acquerrons de l'expérience dans la récupération des données depuis la base, et l'utilisation des modèles de vue (templates).
-
6e partie du tutoriel Express : travail avec les formulaires
-
Dans ce tutoriel nous verrons comment travailler avec les formulaires HTML dans Express, à l'aide de Pug, et en particulier comment concevoir des formulaires pour créer, actualiser et détruire des documents dans la base de données.
-
7e partie du tutoriel Express : déploiement de l'application en production
-
Maintenant que nous avons créé un fantastique site internet pour notre bibliothèque locale, nous allons vouloir installer le serveur du site public pour qu'il soit accessible par les employés de la bibliothèque et les adhérents depuis n'importe où par le Net. Cet article fournit un aperçu de comment trouver un hébergement pour déployer son site web et ce que vous avez à faire pour préparer votre site à la production.
-
- -

Ajout de tutoriels

- -

Tous les tutoriaux de ce module sont listés ci-avant, si vous souhaitez compléter ce module, vous pouvez contribuer avec d'autres tutoriels, notamment sur :

- - - -

Et bien sûr, il serait excellent d'avoir une évaluation !

\ No newline at end of file diff --git a/files/fr/learn/server-side/express_nodejs/index.md b/files/fr/learn/server-side/express_nodejs/index.md new file mode 100644 index 0000000000..abd2352b57 --- /dev/null +++ b/files/fr/learn/server-side/express_nodejs/index.md @@ -0,0 +1,66 @@ +--- +title: Express Web Framework (Node.js/JavaScript) +slug: Learn/Server-side/Express_Nodejs +tags: + - Beginner + - CodingScripting + - Express + - Express.js + - Intro + - JavaScript + - Learn + - Node + - Server-side programming + - node.js +translation_of: Learn/Server-side/Express_Nodejs +--- +
{{LearnSidebar}}
+ +

Express est une infrastructure d'application (framework), écrit en JavaScript et hébergée dans l'environnement d'exécution node.js. Cette section explique certains de ses principaux avantages, comment configurer votre environnement de développement et comment effectuer des tâches courantes de développement et de déploiement.

+ +

Prérequis

+ +

Avant d'aller plus loin, vous devrez avoir compris ce qu'est la programmation côté serveur et le concept de "framework", idéalement en ayant lu notre article Premiers pas en programmation côté-serveur. Une connaissance générale des concepts de programmation et du JavaScript est vivement recommandée, sans pour autant être essentielle à la compréhension des concepts fondamentaux.

+ +
+

Note : Le site MDN possède de nombreuses ressources utiles pour apprendre JavaScript dans le contexte du développement côté client : Référence JavaScript, Guide JavaScript, Les bases de JavaScript, Apprendre JavaScript. Le noyau du langage et les principaux concepts de JavaScript sont les mêmes que pour le développement côté serveur sur Node.js, il est donc important d'avoir déjà une bonne compréhension de JavaScript avant de continuer. Node.js offre des API supplémentaires pour supporter des fonctionnalités utiles dans des environnements sans navigateur, par ex. pour créer des serveurs HTTP et accéder au système de fichiers, mais ne prend pas en charge les API JavaScript pour travailler avec le navigateur et le DOM.

+ +

Ce guide vous fournira des informations sur l'utilisation de Node.js et Express, et contient de nombreuses références à d'autres excellentes ressources sur Internet et dans des livres — dont des liens depuis How do I get started with Node.js (StackOverflow) et What are the best resources for learning Node.js? (Quora).

+
+ +

Guides

+ +
+
Introduction à Express/Node
+
Dans ce premier article sur Express nous répondons aux questions « Qu'est-ce que Node ? » et « Qu'est-ce qu'Express ? » et donnerons une vue d'ensemble de ce qui rend cette infrastructure d'application Express si spéciale. Nous décrirons les principales fonctionnalités et montrerons quelques-uns des principaux éléments constitutifs d'une application Express (bien qu'à ce stade, vous n'aurez pas encore un environnement de développement dans lequel la tester)
+
Installer un environnement de développement pour Node (Express)
+
Maintenant que nous savons à quoi sert Express, nous allons vous apprendre à installer et tester un environnement de développement Node/Express sur Windows, Linux (Ubuntu), et macOS. Quel que soit votre système d'exploitation, cet article devrait vous fournir le nécessaire pour commencer le développement d'applications Express.
+
Tutoriel Express : le site d'une bibliothèque locale
+
Le premier article de notre série de tutoriels explique ce que vous allez apprendre et fournit l'aperçu d'un exemple de site pour une bibliothèque municipale locale. Nous ferons évoluer cet exemple au fur et à mesure des articles suivants.
+
2e partie du tutoriel Express : création d'un squelette de site web
+
Cet article explique comment vous pouvez créer un « squelette » de projet de site web, que vous pouvez alors étoffer de routes spécifiques, de modèles de vues et de bases de données.
+
3e partie du tutoriel Express : utilisation d'une base de données (avec Mongoose)
+
Cet article introduit brièvement le concept de base de données pour Node/Express. Il se poursuit en décrivant comment on peut utiliser Mongoose pour fournir un accès à la base de données de notre site internet de bibliothèque locale. Il explique comment les schémas de données et les modèles sont déclarées, les types principaux des champs de données, et les validations de base. Il donne aussi un aperçu rapide de quelques moyens d'accéder aux données.
+
4e partie du tutoriel Express : les routes et les contrôleurs
+
Dans ce tutoriel nous programmerons des routes (traitements des URL) avec des fonctions factices de traitement pour tous les points de sortie dont nous pourrions éventuellement avoir besoin pour notre site web de la bibliothèque locale. Nous obtiendrons ainsi une structure modulaire pour notre code de traitement par les routes, que nous pourrons étendre avec de vraies fonctions de traitement dans les articles suivants. Nous acquerrons de cette manière une réelle compréhension de la création modulaire des routes avec Express.
+
5e partie du tutoriel Express : affichage des données de la bibliothèque
+
Nous sommes prêts maintenant à ajouter des pages qui afficheront les livres et autres données de notre bibliothèque locale sur son site internet. Ces pages incluront notamment une page d'accueil qui indiquera le nombre d'enregistrements pour chaque type de modèle de donnée, une liste et des pages de détail pour chacun de ces modèles. Chemin faisant, nous acquerrons de l'expérience dans la récupération des données depuis la base, et l'utilisation des modèles de vue (templates).
+
6e partie du tutoriel Express : travail avec les formulaires
+
Dans ce tutoriel nous verrons comment travailler avec les formulaires HTML dans Express, à l'aide de Pug, et en particulier comment concevoir des formulaires pour créer, actualiser et détruire des documents dans la base de données.
+
7e partie du tutoriel Express : déploiement de l'application en production
+
Maintenant que nous avons créé un fantastique site internet pour notre bibliothèque locale, nous allons vouloir installer le serveur du site public pour qu'il soit accessible par les employés de la bibliothèque et les adhérents depuis n'importe où par le Net. Cet article fournit un aperçu de comment trouver un hébergement pour déployer son site web et ce que vous avez à faire pour préparer votre site à la production.
+
+ +

Ajout de tutoriels

+ +

Tous les tutoriaux de ce module sont listés ci-avant, si vous souhaitez compléter ce module, vous pouvez contribuer avec d'autres tutoriels, notamment sur :

+ + + +

Et bien sûr, il serait excellent d'avoir une évaluation !

\ No newline at end of file diff --git a/files/fr/learn/server-side/express_nodejs/introduction/index.html b/files/fr/learn/server-side/express_nodejs/introduction/index.html deleted file mode 100644 index 4f8719e813..0000000000 --- a/files/fr/learn/server-side/express_nodejs/introduction/index.html +++ /dev/null @@ -1,521 +0,0 @@ ---- -title: Introduction à Express/Node -slug: Learn/Server-side/Express_Nodejs/Introduction -tags: - - Beginner - - CodingScripting - - Express - - Learn - - Node - - nodejs - - server-side -translation_of: Learn/Server-side/Express_Nodejs/Introduction ---- -
{{LearnSidebar}}
- -
{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}
- -

Dans ce tout premier article consacré à Express, nous répondons aux questions « Qu'est-ce que Node ? » et « Qu'est-ce que Express ? », et vous donnons un aperçu de ce qui fait d'Express un framework web si spécial. Nous décrivons les principales fonctionnalités et montrons quelques-uns des principaux composants d'une application Express (bien que vous ne disposiez pas encore d'un environnement de développement pour le tester).

- - - - - - - - - - - - -
Prérequis :Une culture de base en informatique, une compréhension globale de la programmation côté serveur et, en particulier, les mécanismes d'interactions client-serveur dans un site web.
Objectif :Devenir familier avec ce qu'est Express et comment il s'intègre dans Node, les fonctionnalités qu'il apporte, et les principales étapes pour construire une application Express.
- -

Introduction à Node

- -

Node (ou plus formellement Node.js) est un environnement d'exécution open-source, multi-plateforme, qui permet aux développeuses et développeurs de créer toutes sortes d'applications et d'outils côté serveur en JavaScript. Cet environnement est destiné à être utilisé en dehors du navigateur (il s'exécute directement sur son ordinateur ou dans le système d'exploitation du serveur). Aussi, Node ne permet pas d'utiliser les API JavaScript liées au navigateur mais des API plus traditionnellement utilisées sur un serveur dont notamment celles pour HTTP ou la manipulation de systèmes de fichier.

- -

Dans une perspective de développement de serveur web, Node présente un certain nombre d'avantages :

- - - -

Vous pouvez utiliser Node.js pour créer un simple serveur web en utilisant l'API Node HTTP.

- -

Hello Node.js

- -

L'exemple qui suit crée un serveur web qui écoute toutes sortes de requêtes HTTP sur l'URL https://127.0.0.1:8000/. Quand une requête est reçue, le script répond avec la chaine « Salut tout le monde ». Si vous avez déjà installé Node, suivez les étapes de l'exemple suivant :

- -
    -
  1. Ouvrez un terminal (sur Windows, ouvrez l'invite de commande (cmd)),
  2. -
  3. Créez le dossier où vous voulez sauvegarder le programme, appelez-le par exemple test-node et placez-vous dedans en utilisant la commande suivante dans votre console : -
    cd test-node
  4. -
  5. Dans votre éditeur de texte favori, créez un fichier nommé "hello.js" et collez ce qui suit dedans : -
    // Charge le module HTTP
    -const http = require("http");
    -
    -const hostname = "127.0.0.1";
    -const port = 8000;
    -
    -// Crée un serveur HTTP
    -const server = http.createServer((req, res) => {
    -
    -  // Configure l'en-tête de la réponse HTTP
    -  // avec le code du statut et le type de contenu
    -  res.writeHead(200, {'Content-Type': 'text/plain'});
    -
    -  // Envoie le corps de la réponse « Salut tout le monde »
    -   res.end('Salut tout le monde\n');
    -})
    -
    -// Démarre le serveur à l'adresse 127.0.0.1 sur le port 8000
    -// Affiche un message dès que le serveur commence à écouter les requêtes
    -server.listen(port, hostname, () => {
    -  console.log(`Le serveur tourne à l'adresse https://${hostname}:${port}/`);
    -})
  6. -
  7. Sauvegardez le fichier dans le dossier créé plus haut.
  8. -
  9. Retournez au terminal et tapez : -
    node hello.js
    -
  10. -
- -

Puis saisissez l'URL "https://localhost:8000" dans votre navigateur. Vous devriez alors voir "Salut tout le monde" en haut à gauche d'une page web ne contenant rien d'autre que ce texte.

- -

Les frameworks web

- -

D'autres tâches de développement web ne sont pas directement prises en charge par Node de façon native. Si vous voulez ajouter différentes manipulations pour divers requêtes HTTP (GET, POST, DELETE, etc.), gérer différemment des requêtes vers plusieurs chemins URL ("routes"), servir des pages statiques ou utiliser des modèles pour créer dynamiquement la réponse, alors vous devrez écrire tout le code vous-même ou, pour éviter de réinventer la roue, vous servir des cadres applicatifs web (frameworks).

- -

Introduction à Express

- -

Express est le framework actuellement le plus populaire dans Node et est la bibliothèque sous-jacente pour un grand nombre d'autres cadres applicatifs web pour Node. Il fournit des mécanismes pour :

- - - -

Bien qu'Express soit assez minimaliste, des middlewares (fonctions intermédiaires) compatibles ont été créés pour résoudre quasiment tous les problèmes de développement web. Il existe des bibliothèques pour se servir des cookies, gérer les sessions, la connexion de l'utilisateur, les paramètres de l'URL, les données POST, les entêtes de sécurité et d'autres encore. Vous trouverez une liste des paquets maintenus par l'équipe Express ici : Express Middleware (ainsi que la liste de paquets tiers populaires).

- -
-

Note : Cette flexibilité est à double tranchant. Il y a une multitude de paquets pour résoudre chaque problème mais trouver le bon paquet à utiliser peut vite devenir un défi. Il n'y a pas non plus de « bonne manière » pour structurer une application et beaucoup d'exemples que vous trouverez sur le net ne sont pas optimisés ou montrent seulement une infime partie de ce que vous devez faire pour développer une application web.

-
- -

D'où viennent Node et Express ?

- -

À ses débuts en 2009, Node a été publié pour Linux uniquement. Le gestionnaire de paquets NPM est sorti en 2010, et le support natif de Windows fut ajouté en 2012. Ceci est un très court aperçu d'une aventure riche en rebondissements. Allez creuser ça sur Wikipédia si vous voulez en savoir plus.

- -

Express est sorti pour la première fois en novembre 2010. Vous pouvez consulter la liste des modifications pour plus d'informations sur la version courante et GitHub pour plus de détails sur l'historique des publications.

- - - -

La popularité d'un framework web est importante car elle conditionne la maintenance dans le temps et les ressources qu'il est raisonnable de mettre à disposition dans la documentation, les bibliothèques d'extensions et le support technique.

- -

Il n'existe pas d'échelle de mesures définitive et fiable pour l'estimation de la popularité des frameworks côté serveur, bien que des sites comme Hot Frameworks essaient d'estimer la popularité par le comptage du nombre de projets GitHub ou StackOverflow. La question est : « Est-ce que Node et Express sont suffisamment populaires pour pouvoir s'affranchir des plateformes non-populaires ? Continuent-ils à évoluer ? Pouvez-vous avoir de l'aide si besoin ? Existe-t-il une opportunité pour vous de gagner de l'argent si vous apprenez Express ? ».

- -

Si on se réfère à la liste des entreprises utilisant Express, la quantité de gens contribuant au code et le nombre de gens fournissant un support payant ou bien gratuit, alors oui, Express est un framework populaire !

- -

Express est-il « dogmatique » ?

- -

Les cadres logiciels web se décrivent souvent comme ayant ou non des opinions données sur tel ou tel sujet au sens où ils sont orientés dans leur usage selon ces choix/opinions. En anglais, on dit d'un framework qu'il est opinionated ou non.

- -

Les frameworks avec une opinion sont ceux qui ont un avis arrêté sur la « bonne manière » de gérer certaines tâches. Ils fournissent souvent un cadre permettant de développer rapidement dans un domaine particulier (résolvant des problèmes d'un type particulier) parce que la bonne manière de faire quoi que ce soit est généralement bien comprise et bien documentée. Toutefois, ils peuvent manquer de flexibilité pour la résolution de problèmes hors de leur portée et tendent à offrir peu de choix concernant les composants et approches qu'ils peuvent utiliser.

- -

Les frameworks sans opinion, par contraste, ont beaucoup moins de restrictions sur la meilleure manière d'assembler des composants ensemble pour atteindre un objectif, ou encore sur les composants que vous devriez utiliser. Ils laissent aux développeurs la possibilité d'utiliser les outils les plus adaptés pour achever une tâche particulière, bien que cela nécessite que vous cherchiez et trouviez ces composants par vous-même.

- -

Express n'est pas particulièrement dogmatique. Vous pouvez intégrer quasiment n'importe quelle fonction intermédiaire compatible voulue dans la pile de gestion des requêtes, dans quasiment n'importe quel ordre. Vous pouvez structurer l'application en un fichier comme en plusieurs, et utiliser n'importe quelle structure de dossiers. Vous pourrez même quelques fois vous sentir perdu⋅e par la liberté que vous avez de vous organiser comme vous le souhaitez !

- -

À quoi ressemble du code Express ?

- -

Dans un site web utilisé pour traiter des données, une application web attend des requêtes HTTP du navigateur web (ou d'un autre client). Quand une requête est reçue, l'application cherche quelle action est requise en fonction du modèle de l'URL et des possibles informations associés contenues dans les données POST ou GET. Selon ce qui est requis, il pourra alors lire ou écrire des informations dans une base de données ou effectuer d'autre tâches pour satisfaire la requête. L'application va alors retourner une réponse au navigateur web, souvent une page HTML créée dynamiquement pour le navigateur, en intégrant les données récupérées dans un modèle HTML.

- -

Express fournit des méthodes pour spécifier quelle fonction est appelée pour une méthode HTTP particulière (GET, POST, SET, etc.) et un modèle d'URL ("Route"), ainsi que des méthodes pour spécifier quel moteur de rendu de vues ("view") est utilisé, où sont les modèles de vues et quel modèle utiliser pour générer une réponse. Vous pouvez utiliser les fonctions intermédiaires d'Express pour prendre en charge les cookies, les sessions, les utilisateurs, obtenir les paramètres POST/GET, etc. Vous pouvez utiliser n'importe que système de base données supporté par Node (Express ne définit aucun comportement relatif aux bases de données).

- -

Les sections suivantes expliquent quelques choses communes que vous verrez en travaillant avec du code Express et Node.

- -

Hello World Express

- -

Tout d'abord intéressons-nous à l'exemple Hello World standard d'Express (nous expliquons chaque partie de cet exemple ci-dessous, et dans les sections suivantes).

- -
-

Note : Si vous avez déjà installé Node et Express (ou si vous les installez comme montré dans l'article suivant), vous pouvez enregistrer ce code dans un fichier texte appelé app.js et l'exécuter via un terminal en tapant :

-

node app.js

-
- -
const express = require('express');
-const app = express();
-const port = 3000;
-
-app.get('/', (req, res) => {
-  res.send('Hello World!')
-});
-
-app.listen(port, () => {
-  console.log(`Application exemple à l'écoute sur le port ${port}!`)
-});
-
- -

Les deux premières lignes importent (avec require()) le module express et créent une application express. Cet objet, qui est traditionnellement nommé app, possède des méthodes pour acheminer les requêtes HTTP, configurer les intergiciels, rendre les vues HTML, enregistrer un moteur de modèles et modifier les paramètres de l'application qui contrôlent le comportement de l'application (par exemple, le mode d'environnement, si les définitions de route sont sensibles à la casse, etc.).

- -

La partie centrale du code (les trois lignes commençant par app.get) montre une définition de route. La méthode app.get() spécifie une fonction de rappel qui sera invoquée chaque fois qu'il y aura une requête HTTP GET avec un chemin ('/') relatif à la racine du site. La fonction de rappel prend une requête et un objet réponse comme arguments, et appelle simplement send() sur la réponse pour renvoyer la chaîne de caractères "Hello World!".

- -

Le dernier bloc démarre le serveur sur le port 3000 et affiche un commentaire de journal dans la console. Avec le serveur en cours d'exécution, vous pouvez aller sur localhost:3000 dans votre navigateur pour voir l'exemple de réponse renvoyé.

- -

Créer et importer des modules

- -

Un module est une bibliothèque/fichier JavaScript que vous pouvez importer dans un autre code en utilisant la fonction require() de Node. Express lui-même est un module, tout comme les bibliothèques de middleware et de base de données que nous utilisons dans nos applications Express.

- -

Le code ci-dessous montre comment nous importons un module par son nom, en utilisant le framework Express comme exemple. Tout d'abord, nous invoquons la fonction require(), en spécifiant le nom du module sous forme de chaîne ('express'), et en appelant l'objet retourné pour créer une applicationExpress. Nous pouvons alors accéder aux propriétés et fonctions de l'objet application.

- -
const express = require('express');
-const app = express();
-
- -

Vous pouvez également créer vos propres modules qui peuvent être importés de la même manière.

- -
-

Note : Vous voudrez créer vos propres modules, car cela vous permet d'organiser votre code en parties maniables — une application monolithique à fichier unique est difficile à comprendre et à maintenir. L'utilisation de modules vous aide également à gérer votre espace de noms, car seules les variables que vous exportez explicitement sont importées lorsque vous utilisez un module.

-
- -

Pour rendre les objets disponibles en dehors d'un module, il suffit de les affecter à l'objet exports. Par exemple, le module square.js ci-dessous est un fichier qui exporte les méthodes area() et perimeter() :

- -
exports.area = function(width) { return width * width; };
-exports.perimeter = function(width) { return 4 * width; };
- -

Nous pouvons importer ce module en utilisant require(), puis appeler la ou les méthodes exportées comme indiqué :

- -
var square = require('./square'); // Ici, nous demandons le nom du fichier sans l'extension de fichier .js (facultative).
-console.log("L'aire d'un carré dont la largeur est de 4 est la suivante " + square.area(4));
- -
-

Note : Vous pouvez également spécifier un chemin absolu vers le module (ou un nom, comme nous l'avons fait initialement).

-
- -

Si vous souhaitez exporter un objet complet en une seule affectation au lieu de le construire une propriété à la fois, affectez-le à module.exports comme indiqué ci-dessous (vous pouvez également procéder ainsi pour faire de la racine de l'objet exports un constructeur ou une autre fonction) :

- -
module.exports = {
-  area: function(width) {
-    return width * width;
-  },
-
-  perimeter: function(width) {
-    return 4 * width;
-  }
-};
- -
-

Note : L'objet exports peut être vu comme un raccourci vers module.exports au sein d'un module donné. En fait, exports est simplement une variable qui est initialisée avec la valeur de module.exports avant que le module soit évalué. Cette valeur est une référence vers un objet. Cela signifie que exports référence le même objet que celui référencé par module.exports. Cela signifie également qu'affecter une autre valeur à exports le détachera complètement de module.exports.

-
- - -

Pour de plus amples informations sur les modules, voir Modules (documentation Node).

- -

Utilisation des API asynchrones

- -

Le code JavaScript utilise fréquemment des API asynchrones plutôt que synchrones pour les opérations qui peuvent prendre un certain temps à se terminer. Une API synchrone est une API dans laquelle chaque opération doit être terminée avant que l'opération suivante puisse commencer. Par exemple, les fonctions d'enregistrement suivantes sont synchrones et impriment le texte dans la console dans l'ordre (Premier, Second).

- -
console.log('Premier');
-console.log('Second');
- -

En revanche, une API asynchrone est une API qui lance une opération et revient immédiatement (avant que l'opération ne soit terminée). Une fois l'opération terminée, l'API utilisera un mécanisme quelconque pour effectuer des opérations supplémentaires. Par exemple, le code ci-dessous imprimera « Second, Premier » car même si la méthode setTimeout() est appelée en premier, et revient immédiatement, l'opération ne se termine pas avant plusieurs secondes.

- -
setTimeout(function() {
-  console.log('Premier');
-}, 3000);
-console.log('Second');
- -

L'utilisation d'API asynchrones non bloquantes est encore plus importante sur Node que dans le navigateur, car Node est un environnement d'exécution événementiel avec un seul thread. Cela signifie que toutes les requêtes adressées au serveur sont exécutées sur le même thread (plutôt que d'être fractionnées en threads distincts). Ce modèle est extrêmement efficace en termes de vitesse et de ressources du serveur, mais il signifie que si l'une de vos fonctions appelle des méthodes synchrones qui prennent beaucoup de temps pour se terminer, elle bloquera non seulement la demande actuelle, mais aussi toutes les autres demandes traitées par votre application Web.

- -

Il existe plusieurs façons pour une API asynchrone d'informer votre application de la fin d'une opération. La méthode la plus courante consiste à enregistrer une fonction de rappel lorsque vous invoquez l'API asynchrone, qui sera rappelée lorsque l'opération sera terminée. C'est l'approche utilisée ci-dessus.

- -
-

Note : L'utilisation des rappels (« callbacks ») peut être assez « désordonnée » si vous avez une séquence d'opérations asynchrones dépendantes qui doivent être exécutées dans l'ordre, car cela entraîne de multiples niveaux de rappels imbriqués. Ce problème est communément appelé « l'enfer des callbacks ». Ce problème peut être réduit par de bonnes pratiques de codage dont l'utilisation des promesses ou de async/await.

-
- -
-

Note : Une convention courante pour Node et Express est d'utiliser des callbacks de type error-first. Dans cette convention, la première valeur de vos fonctions de rappel est une valeur d'erreur, tandis que les arguments suivants contiennent des données de succès. Il y a une bonne explication de l'utilité de cette approche dans ce blog : The Node.js Way - Comprendre les callbacks de type « Error First ». (fredkschott.com).

-
- -

Créer des gestionnaires de route

- -

Dans notre exemple Hello World d'Express (voir ci-dessus), nous avons défini une fonction de gestion de route (un callback) pour les requêtes HTTP GET vers la racine du site ('/').

- -
app.get('/', (req, res) => {
-  res.send('Hello World!');
-});
- -

La fonction de rappel prend une requête et un objet réponse comme arguments. Dans ce cas, la méthode appelle simplement send() sur la réponse pour renvoyer la chaîne de caractères « Hello World ! ». Il existe un nombre d'autres méthodes de réponse pour terminer le cycle requête/réponse, par exemple vous pourriez appeler res.json() pour envoyer une réponse JSON ou res.sendFile() pour envoyer un fichier.

- -
-

Note : Vous pouvez utiliser les noms d'arguments de votre choix dans les fonctions de rappel ; lorsque le rappel est invoqué, le premier argument sera toujours la requête et le second sera toujours la réponse. Il est judicieux de les nommer de telle sorte que vous puissiez identifier l'objet avec lequel vous travaillez dans le corps du callback.

-
- -

L'objet Express application fournit également des méthodes permettant de définir des gestionnaires de route pour tous les autres verbes HTTP, qui sont pour la plupart utilisés exactement de la même manière :

-

checkout(), copy(), delete(), get(), head(), lock(), merge(), mkactivity(), mkcol(), move(), m-search(), notify(), options(), patch(), post(), purge(), put(), report(), search(), subscribe(), trace(), unlock(), unsubscribe().

- -

Il existe une méthode de routage spéciale, app.all(), qui sera appelée en réponse à toute méthode HTTP. Ceci est utilisé pour charger les fonctions middleware à un chemin particulier pour toutes les méthodes de requête. L'exemple suivant (tiré de la documentation d'Express) montre un gestionnaire qui sera exécuté pour les requêtes vers /secret indépendamment du verbe HTTP utilisé (à condition qu'il soit supporté par le module http).

- -
app.all('/secret', (req, res, next) => {
-  console.log('Accès à la section secrète ...');
-  next(); // passe le contrôle au gestionnaire suivant
-});
- -

Les routes vous permettent de faire correspondre des modèles particuliers de caractères dans une URL, d'extraire certaines valeurs de l'URL et de les transmettre comme paramètres au gestionnaire de la route (en tant qu'attributs de l'objet de la demande transmis comme paramètre).

- -

Il est souvent utile de regrouper les gestionnaires de route pour une partie particulière d'un site et d'y accéder en utilisant un préfixe de route commun (par exemple, un site avec un Wiki pourrait avoir toutes les routes liées au wiki dans un seul fichier et les faire accéder avec un préfixe de route de /wiki/). Dans Express, ceci est réalisé en utilisant l'objet express.Router. Par exemple, nous pouvons créer notre route wiki dans un module nommé wiki.js, puis exporter l'objet Router, comme indiqué ci-dessous :

- -
// wiki.js - Module route du wiki
-
-const express = require('express');
-const router = express.Router();
-
-// Route vers la page d'accueil
-router.get('/', (req, res) => {
-  res.send("Page d'accueil du wiki");
-});
-
-
-// Route vers la page à propos
-router.get('/about', (req, res) => {
-  res.send('À propos de ce wiki');
-});
-
-module.exports = router;
- -
-

Note : L'ajout de routes à l'objet Router est identique à l'ajout de routes à l'objet app (comme indiqué précédemment).

-
- -

Pour utiliser le routeur dans notre fichier d'application principal, nous devrions alors require() le module route (wiki.js), puis appeler use() sur l'application Express pour ajouter le routeur au chemin de manipulation du middleware. Les deux routes seront alors accessibles depuis /wiki/ et /wiki/about/.

- -
const wiki = require('./wiki.js');
-// ...
-app.use('/wiki', wiki);
- -

Nous vous montrerons beaucoup plus en détails comment travailler avec les routes, et en particulier comment utiliser le Router, plus tard dans la section liée Routes et contrôleurs.

- -

Utilisation d'un middleware/intergiciel

- -

L'intergiciel (aussi appelé « middleware ») est largement utilisé dans les applications d'Express, pour des tâches allant du service de fichiers statiques à la gestion des erreurs, en passant par la compression des réponses HTTP. Alors que les fonctions de route terminent le cycle requête-réponse HTTP en renvoyant une réponse au client HTTP, les fonctions d'intergiciel effectuent typiquement une opération sur la demande ou la réponse, puis appellent la fonction suivante dans la « pile », qui peut être un autre intergiciel ou un gestionnaire de route. L'ordre dans lequel les intergiciels sont appelés dépend du code de l'application.

- -
-

Note : L'intergiciel peut effectuer n'importe quelle opération, exécuter n'importe quel code, apporter des modifications à l'objet requête et réponse, et il peut aussi mettre fin au cycle requête-réponse. S'il ne met pas fin au cycle, alors il doit appeler next() pour passer le contrôle à la fonction suivante de l'intergiciel (ou la requête sera laissée en suspens).

-
- -

La plupart des apps utiliseront des programmes intermédiaires tiers afin de simplifier les tâches courantes de développement web comme le travail avec les cookies, les sessions, l'authentification des utilisateurs, l'accès aux données POST et JSON des requêtes, la journalisation, etc. Vous pouvez trouver une liste des paquets middleware maintenus par l'équipe Express (qui inclut également d'autres paquets populaires de tiers). D'autres paquets Express sont disponibles sur le gestionnaire de paquets NPM.

- -

Pour utiliser un middleware tiers, vous devez d'abord l'installer dans votre application à l'aide de NPM. Par exemple, pour installer l'intergiciel de journalisation des requêtes HTTP morgan, vous devez procéder comme suit :

- -
npm install morgan
- -

Vous pourriez alors appeler use() sur l'objet Express application pour ajouter l'intergiciel à la pile :

- -
const express = require('express');
-const logger = require('morgan');
-const app = express();
-app.use(logger('dev'));
-...
- -
-

Note : Les fonctions d'intergiciel et de routage sont appelées dans l'ordre où elles sont déclarées. Pour certains intergiciels, l'ordre est important (par exemple, si l'intergiciel de session dépend de l'intergiciel de cookie, alors le gestionnaire de cookie doit être ajouté en premier). Il est presque toujours nécessaire d'appeler l'intergiciel avant de définir les routes, sinon vos gestionnaires de routes n'auront pas accès aux fonctionnalités ajoutées par votre intergiciel.

-
- -

Vous pouvez écrire vos propres fonctions intergiciels, et vous serez probablement amené à le faire (ne serait-ce que pour créer un code de gestion des erreurs). La seule différence entre une fonction middleware et un callback de gestionnaire de route est que les fonctions middleware ont un troisième argument next, que les fonctions middleware sont censées appeler si elles ne sont pas celle qui termine le cycle de requête (lorsque la fonction middleware est appelée, cela contient la fonction next qui doit être appelée).

- -

Vous pouvez ajouter une fonction d'intergiciel à la chaîne de traitement avec app.use() ou app.add(), selon que vous voulez appliquer l'intergiciel à toutes les réponses ou aux réponses avec un verbe HTTP particulier (GET, POST, etc). Vous spécifiez les routes de la même manière dans les deux cas, bien que la route soit facultative lors de l'appel à app.use().

- -

L'exemple ci-dessous montre comment vous pouvez ajouter la fonction middleware en utilisant les deux méthodes, et avec/sans route.

- -
const express = require('express');
-const app = express();
-
-// Un exemple de fonction middleware
-let a_middleware_function = function(req, res, next) {
-  // ... effectuer certaines opérations
-  next(); // Appelez next() pour qu'Express appelle la fonction middleware suivante dans la chaîne.
-}
-
-// Fonction ajoutée avec use() pour toutes les routes et verbes
-app.use(a_middleware_function);
-
-// Fonction ajoutée avec use() pour une route spécifique
-app.use('/uneroute', a_middleware_function);
-
-// Une fonction middleware ajoutée pour un verbe et une route HTTP spécifiques
-app.get('/', a_middleware_function);
-
-app.listen(3000);
- -
-

Note : Ci-dessus, nous déclarons la fonction middleware séparément, puis nous la définissons comme fonction de rappel. Dans notre précédente fonction de gestion de route, nous avons déclaré la fonction de rappel lorsqu'elle a été utilisée. En JavaScript, les deux approches sont valables.

-
- -

La documentation d'Express contient beaucoup d'autres excellents documents sur l'utilisation et l'écriture d'intergiciels Express.

- -

Servir les fichiers statiques

- -

Vous pouvez utiliser l'intergiciel express.static pour servir des fichiers statiques, notamment vos images, CSS et JavaScript (static() est la seule fonction de l'intergiciel qui fait réellement partie d'Express). Par exemple, vous utiliserez la ligne ci-dessous pour servir des images, des fichiers CSS et des fichiers JavaScript à partir d'un répertoire nommé 'public' au même niveau que celui où vous appelez node :

- -
app.use(express.static('public'));
- -

Tous les fichiers du répertoire public sont servis en ajoutant leur nom de fichier (relatif au répertoire "public" de base) à l'URL de base. Ainsi, par exemple :

- -
https://localhost:3000/images/dog.jpg
-https://localhost:3000/css/style.css
-https://localhost:3000/js/app.js
-https://localhost:3000/about.html
- -

Vous pouvez appeler static() plusieurs fois pour servir plusieurs répertoires. Si un fichier ne peut pas être trouvé par une fonction middleware, alors il sera simplement transmis au middleware suivant (l'ordre dans lequel le middleware est appelé est basé sur votre ordre de déclaration).

- -
app.use(express.static('public'));
-app.use(express.static('media'));
- -

Vous pouvez également créer un préfixe virtuel pour vos URL statiques, plutôt que de voir les fichiers ajoutés à l'URL de base. Par exemple, ici nous spécifions un chemin de montage pour que les fichiers soient chargés avec le préfixe « /media » :

- -
app.use('/media', express.static('public'));
- -

Maintenant, vous pouvez charger les fichiers qui se trouvent dans le répertoire public à partir du préfixe du chemin /media.

- -
https://localhost:3000/media/images/dog.jpg
-https://localhost:3000/media/video/cat.mp4
-https://localhost:3000/media/cry.mp3
- -
-

Note : Voir également Servir des fichiers statiques dans Express.

-
- -

Traitement des erreurs

- -

Les erreurs sont traitées par une ou plusieurs fonctions spéciales du middleware qui ont quatre arguments, au lieu des trois habituels : (err, req, res, next). Par exemple :

- -
app.use(function(err, req, res, next) {
-  console.error(err.stack);
-  res.status(500).send("Quelque chose s'est cassé !");
-});
- -

Ceux-ci peuvent retourner tout contenu nécessaire, mais doivent être appelés après tous les autres app.use() et les appels de routes afin qu'ils soient le dernier middleware dans le processus de traitement des requêtes !

- -

Express est livré avec un gestionnaire d'erreurs intégré, qui prend en charge toutes les erreurs restantes qui pourraient être rencontrées dans l'application. Cette fonction middleware de gestion des erreurs par défaut est ajoutée à la fin de la pile de fonctions middleware. Si vous passez une erreur à next() et que vous ne la gérez pas dans un gestionnaire d'erreurs, elle sera traitée par le gestionnaire d'erreurs intégré ; l'erreur sera écrite au client avec la trace de la pile.

- -
-

Note : La trace de la pile n'est pas incluse dans l'environnement de production. Pour exécuter une application serveur Express, la variable d'environnement NODE_ENV doit être définie avec la valeur production.

-
- -
-

Note : Les codes d'état HTTP 404 et autres « erreurs » ne sont pas traités comme des erreurs. Si vous voulez les gérer, vous pouvez ajouter une fonction middleware pour le faire. Pour plus d'informations, consultez la FAQ.

-
- -

Pour plus d'informations, voir Gestion des erreurs (docs Express).

- -

Utilisation des bases de données

- -

Les apps Express peuvent utiliser tout mécanisme de base de données pris en charge par Node (Express lui-même ne définit aucun comportements/exigences supplémentaire spécifique pour la gestion des bases de données). Il existe de nombreuses options, notamment PostgreSQL, MySQL, Redis, SQLite, MongoDB, etc.

- -

Pour les utiliser, vous devez d'abord installer le pilote de base de données à l'aide de NPM. Par exemple, pour installer le pilote de la populaire base de données NoSQL MongoDB, vous devez utiliser la commande suivante :

- -
$ npm install mongodb
- -

La base de données elle-même peut être installée localement ou sur un serveur en nuage. Dans votre code Express, vous avez besoin du pilote, vous vous connectez à la base de données, puis vous effectuez des opérations de création, lecture, mise à jour et suppression (en anglais, on utilise l'acronyme CRUD qui signifie Create, Read, Update, Delete). L'exemple ci-dessous (tiré de la documentation d'Express) montre comment vous pouvez trouver des enregistrements « mammifères » en utilisant MongoDB.

- -
// cela fonctionne avec les anciennes versions de mongodb version ~ 2.2.33
-const MongoClient = require('mongodb').MongoClient;
-
-MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
-  if (err) throw err;
-
-  db.collection('mammals').find().toArray(function (err, result) {
-    if (err) throw err;
-
-    console.log(result);
-  });
-});
-
-// pour mongodb version 3.0 et supérieure
-const MongoClient = require('mongodb').MongoClient;
-MongoClient.connect('mongodb://localhost:27017/animals', function(err, client){
-   if(err) throw err;
-
-   let db = client.db('animals');
-   db.collection('mammals').find().toArray(function(err, result){
-     if(err) throw err;
-     console.log(result);
-     client.close();
-   });
-});
- -

Une autre approche populaire consiste à accéder à votre base de données de manière indirecte, via un mappeur objet-relationnel (« ORM »). Dans cette approche, vous définissez vos données en tant qu'objets ou modèles et l'ORM les met en correspondance avec le format de base de données sous-jacent. L'avantage de cette approche est qu'en tant que développeur, vous pouvez continuer à penser en termes d'objets JavaScript plutôt qu'en termes de sémantique de base de données, et qu'il existe un endroit évident pour effectuer la validation et la vérification des données entrantes. Nous parlerons davantage des bases de données dans un article ultérieur.

- -

Pour plus d'informations, voir Intégration de base de données (docs Express).

- -

Rendu des données (vues)

- -

Les moteurs de modèles (appelés « moteurs de vue » par Express) vous permettent de spécifier la structure d'un document de sortie dans un modèle, en utilisant des espaces réservés pour les données qui seront remplies lorsqu'une page sera générée. Les modèles sont souvent utilisés pour créer du HTML, mais peuvent également créer d'autres types de documents. Express prend en charge un certain nombre de moteurs de modèles, et il existe une comparaison utile des moteurs les plus populaires ici : Comparaison des moteurs de création de modèles JavaScript : Jade, Mustache, Dust et plus.

- -

Dans le code des paramètres de votre application, vous définissez le moteur de modèles à utiliser et l'emplacement où Express doit rechercher les modèles à l'aide des paramètres « views » et « view engines », comme indiqué ci-dessous (vous devrez également installer le paquet contenant votre bibliothèque de modèles !)

- -
const express = require('express');
-const path = require('path');
-const app = express();
-
-// Définir le répertoire contenant les modèles ('views')
-app.set('views', path.join(__dirname, 'views'));
-
-// Définir le moteur d'affichage à utiliser, dans ce cas 'some_template_engine_name'.
-app.set('view engine', 'some_template_engine_name');
- -

L'apparence du modèle dépendra du moteur que vous utilisez. En supposant que vous ayez un fichier de modèle nommé « index.<template_extension> » qui contient des espaces réservés pour des variables de données nommées « title » et « message », vous appelleriez Response.render() dans une fonction de gestionnaire de route pour créer et envoyer la réponse HTML :

- -
app.get('/', function(req, res) {
-  res.render('index', { title: 'À propos des poules', message: 'Elles sont où ?' });
-});
- -

Pour plus d'informations, voir Utilisation des moteurs de modèles avec Express (docs Express).

- -

Structure du fichier

- -

Express ne fait aucune supposition en termes de structure ou de composants que vous utilisez. Les routes, les vues, les fichiers statiques et toute autre logique spécifique à l'application peuvent vivre dans un nombre quelconque de fichiers avec n'importe quelle structure de répertoire. Bien qu'il soit parfaitement possible d'avoir l'ensemble de l'application Express dans un seul fichier, il est généralement judicieux de diviser votre application en fichiers basés sur la fonction (par exemple, gestion de compte, blogs, forums de discussion) et le domaine de problème architectural (par exemple, modèle, vue ou contrôleur si vous utilisez une architecture MVC).

- -

Dans une prochaine rubrique, nous utiliserons le Générateur d'applications express, qui crée un squelette d'application modulaire que nous pouvons facilement étendre pour créer des applications web.

- -

Résumé

- -

Félicitations, vous avez terminé la première étape de votre voyage Express/Node ! Vous devriez maintenant comprendre les principaux avantages d'Express et de Node, et savoir à quoi ressemblent les principales parties d'une application Express (routes, intergiciels, gestion des erreurs et code modèle). Vous devez également comprendre qu'Express étant un framework non autonome, la manière dont vous assemblez ces éléments et les bibliothèques que vous utilisez dépendent largement de vous !

- -

Bien sûr, Express est délibérément un cadre d'application web très léger, et une grande partie de ses avantages et de son potentiel provient de bibliothèques et de fonctionnalités tierces. Nous les examinerons plus en détail dans les articles suivants. Dans notre prochain article, nous nous pencherons sur la configuration d'un environnement de développement Node, afin que vous puissiez commencer à voir du code Express en action.

- -

Voir aussi

- - - -
{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}
- -

Dans ce module

- - diff --git a/files/fr/learn/server-side/express_nodejs/introduction/index.md b/files/fr/learn/server-side/express_nodejs/introduction/index.md new file mode 100644 index 0000000000..4f8719e813 --- /dev/null +++ b/files/fr/learn/server-side/express_nodejs/introduction/index.md @@ -0,0 +1,521 @@ +--- +title: Introduction à Express/Node +slug: Learn/Server-side/Express_Nodejs/Introduction +tags: + - Beginner + - CodingScripting + - Express + - Learn + - Node + - nodejs + - server-side +translation_of: Learn/Server-side/Express_Nodejs/Introduction +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}
+ +

Dans ce tout premier article consacré à Express, nous répondons aux questions « Qu'est-ce que Node ? » et « Qu'est-ce que Express ? », et vous donnons un aperçu de ce qui fait d'Express un framework web si spécial. Nous décrivons les principales fonctionnalités et montrons quelques-uns des principaux composants d'une application Express (bien que vous ne disposiez pas encore d'un environnement de développement pour le tester).

+ + + + + + + + + + + + +
Prérequis :Une culture de base en informatique, une compréhension globale de la programmation côté serveur et, en particulier, les mécanismes d'interactions client-serveur dans un site web.
Objectif :Devenir familier avec ce qu'est Express et comment il s'intègre dans Node, les fonctionnalités qu'il apporte, et les principales étapes pour construire une application Express.
+ +

Introduction à Node

+ +

Node (ou plus formellement Node.js) est un environnement d'exécution open-source, multi-plateforme, qui permet aux développeuses et développeurs de créer toutes sortes d'applications et d'outils côté serveur en JavaScript. Cet environnement est destiné à être utilisé en dehors du navigateur (il s'exécute directement sur son ordinateur ou dans le système d'exploitation du serveur). Aussi, Node ne permet pas d'utiliser les API JavaScript liées au navigateur mais des API plus traditionnellement utilisées sur un serveur dont notamment celles pour HTTP ou la manipulation de systèmes de fichier.

+ +

Dans une perspective de développement de serveur web, Node présente un certain nombre d'avantages :

+ + + +

Vous pouvez utiliser Node.js pour créer un simple serveur web en utilisant l'API Node HTTP.

+ +

Hello Node.js

+ +

L'exemple qui suit crée un serveur web qui écoute toutes sortes de requêtes HTTP sur l'URL https://127.0.0.1:8000/. Quand une requête est reçue, le script répond avec la chaine « Salut tout le monde ». Si vous avez déjà installé Node, suivez les étapes de l'exemple suivant :

+ +
    +
  1. Ouvrez un terminal (sur Windows, ouvrez l'invite de commande (cmd)),
  2. +
  3. Créez le dossier où vous voulez sauvegarder le programme, appelez-le par exemple test-node et placez-vous dedans en utilisant la commande suivante dans votre console : +
    cd test-node
  4. +
  5. Dans votre éditeur de texte favori, créez un fichier nommé "hello.js" et collez ce qui suit dedans : +
    // Charge le module HTTP
    +const http = require("http");
    +
    +const hostname = "127.0.0.1";
    +const port = 8000;
    +
    +// Crée un serveur HTTP
    +const server = http.createServer((req, res) => {
    +
    +  // Configure l'en-tête de la réponse HTTP
    +  // avec le code du statut et le type de contenu
    +  res.writeHead(200, {'Content-Type': 'text/plain'});
    +
    +  // Envoie le corps de la réponse « Salut tout le monde »
    +   res.end('Salut tout le monde\n');
    +})
    +
    +// Démarre le serveur à l'adresse 127.0.0.1 sur le port 8000
    +// Affiche un message dès que le serveur commence à écouter les requêtes
    +server.listen(port, hostname, () => {
    +  console.log(`Le serveur tourne à l'adresse https://${hostname}:${port}/`);
    +})
  6. +
  7. Sauvegardez le fichier dans le dossier créé plus haut.
  8. +
  9. Retournez au terminal et tapez : +
    node hello.js
    +
  10. +
+ +

Puis saisissez l'URL "https://localhost:8000" dans votre navigateur. Vous devriez alors voir "Salut tout le monde" en haut à gauche d'une page web ne contenant rien d'autre que ce texte.

+ +

Les frameworks web

+ +

D'autres tâches de développement web ne sont pas directement prises en charge par Node de façon native. Si vous voulez ajouter différentes manipulations pour divers requêtes HTTP (GET, POST, DELETE, etc.), gérer différemment des requêtes vers plusieurs chemins URL ("routes"), servir des pages statiques ou utiliser des modèles pour créer dynamiquement la réponse, alors vous devrez écrire tout le code vous-même ou, pour éviter de réinventer la roue, vous servir des cadres applicatifs web (frameworks).

+ +

Introduction à Express

+ +

Express est le framework actuellement le plus populaire dans Node et est la bibliothèque sous-jacente pour un grand nombre d'autres cadres applicatifs web pour Node. Il fournit des mécanismes pour :

+ + + +

Bien qu'Express soit assez minimaliste, des middlewares (fonctions intermédiaires) compatibles ont été créés pour résoudre quasiment tous les problèmes de développement web. Il existe des bibliothèques pour se servir des cookies, gérer les sessions, la connexion de l'utilisateur, les paramètres de l'URL, les données POST, les entêtes de sécurité et d'autres encore. Vous trouverez une liste des paquets maintenus par l'équipe Express ici : Express Middleware (ainsi que la liste de paquets tiers populaires).

+ +
+

Note : Cette flexibilité est à double tranchant. Il y a une multitude de paquets pour résoudre chaque problème mais trouver le bon paquet à utiliser peut vite devenir un défi. Il n'y a pas non plus de « bonne manière » pour structurer une application et beaucoup d'exemples que vous trouverez sur le net ne sont pas optimisés ou montrent seulement une infime partie de ce que vous devez faire pour développer une application web.

+
+ +

D'où viennent Node et Express ?

+ +

À ses débuts en 2009, Node a été publié pour Linux uniquement. Le gestionnaire de paquets NPM est sorti en 2010, et le support natif de Windows fut ajouté en 2012. Ceci est un très court aperçu d'une aventure riche en rebondissements. Allez creuser ça sur Wikipédia si vous voulez en savoir plus.

+ +

Express est sorti pour la première fois en novembre 2010. Vous pouvez consulter la liste des modifications pour plus d'informations sur la version courante et GitHub pour plus de détails sur l'historique des publications.

+ + + +

La popularité d'un framework web est importante car elle conditionne la maintenance dans le temps et les ressources qu'il est raisonnable de mettre à disposition dans la documentation, les bibliothèques d'extensions et le support technique.

+ +

Il n'existe pas d'échelle de mesures définitive et fiable pour l'estimation de la popularité des frameworks côté serveur, bien que des sites comme Hot Frameworks essaient d'estimer la popularité par le comptage du nombre de projets GitHub ou StackOverflow. La question est : « Est-ce que Node et Express sont suffisamment populaires pour pouvoir s'affranchir des plateformes non-populaires ? Continuent-ils à évoluer ? Pouvez-vous avoir de l'aide si besoin ? Existe-t-il une opportunité pour vous de gagner de l'argent si vous apprenez Express ? ».

+ +

Si on se réfère à la liste des entreprises utilisant Express, la quantité de gens contribuant au code et le nombre de gens fournissant un support payant ou bien gratuit, alors oui, Express est un framework populaire !

+ +

Express est-il « dogmatique » ?

+ +

Les cadres logiciels web se décrivent souvent comme ayant ou non des opinions données sur tel ou tel sujet au sens où ils sont orientés dans leur usage selon ces choix/opinions. En anglais, on dit d'un framework qu'il est opinionated ou non.

+ +

Les frameworks avec une opinion sont ceux qui ont un avis arrêté sur la « bonne manière » de gérer certaines tâches. Ils fournissent souvent un cadre permettant de développer rapidement dans un domaine particulier (résolvant des problèmes d'un type particulier) parce que la bonne manière de faire quoi que ce soit est généralement bien comprise et bien documentée. Toutefois, ils peuvent manquer de flexibilité pour la résolution de problèmes hors de leur portée et tendent à offrir peu de choix concernant les composants et approches qu'ils peuvent utiliser.

+ +

Les frameworks sans opinion, par contraste, ont beaucoup moins de restrictions sur la meilleure manière d'assembler des composants ensemble pour atteindre un objectif, ou encore sur les composants que vous devriez utiliser. Ils laissent aux développeurs la possibilité d'utiliser les outils les plus adaptés pour achever une tâche particulière, bien que cela nécessite que vous cherchiez et trouviez ces composants par vous-même.

+ +

Express n'est pas particulièrement dogmatique. Vous pouvez intégrer quasiment n'importe quelle fonction intermédiaire compatible voulue dans la pile de gestion des requêtes, dans quasiment n'importe quel ordre. Vous pouvez structurer l'application en un fichier comme en plusieurs, et utiliser n'importe quelle structure de dossiers. Vous pourrez même quelques fois vous sentir perdu⋅e par la liberté que vous avez de vous organiser comme vous le souhaitez !

+ +

À quoi ressemble du code Express ?

+ +

Dans un site web utilisé pour traiter des données, une application web attend des requêtes HTTP du navigateur web (ou d'un autre client). Quand une requête est reçue, l'application cherche quelle action est requise en fonction du modèle de l'URL et des possibles informations associés contenues dans les données POST ou GET. Selon ce qui est requis, il pourra alors lire ou écrire des informations dans une base de données ou effectuer d'autre tâches pour satisfaire la requête. L'application va alors retourner une réponse au navigateur web, souvent une page HTML créée dynamiquement pour le navigateur, en intégrant les données récupérées dans un modèle HTML.

+ +

Express fournit des méthodes pour spécifier quelle fonction est appelée pour une méthode HTTP particulière (GET, POST, SET, etc.) et un modèle d'URL ("Route"), ainsi que des méthodes pour spécifier quel moteur de rendu de vues ("view") est utilisé, où sont les modèles de vues et quel modèle utiliser pour générer une réponse. Vous pouvez utiliser les fonctions intermédiaires d'Express pour prendre en charge les cookies, les sessions, les utilisateurs, obtenir les paramètres POST/GET, etc. Vous pouvez utiliser n'importe que système de base données supporté par Node (Express ne définit aucun comportement relatif aux bases de données).

+ +

Les sections suivantes expliquent quelques choses communes que vous verrez en travaillant avec du code Express et Node.

+ +

Hello World Express

+ +

Tout d'abord intéressons-nous à l'exemple Hello World standard d'Express (nous expliquons chaque partie de cet exemple ci-dessous, et dans les sections suivantes).

+ +
+

Note : Si vous avez déjà installé Node et Express (ou si vous les installez comme montré dans l'article suivant), vous pouvez enregistrer ce code dans un fichier texte appelé app.js et l'exécuter via un terminal en tapant :

+

node app.js

+
+ +
const express = require('express');
+const app = express();
+const port = 3000;
+
+app.get('/', (req, res) => {
+  res.send('Hello World!')
+});
+
+app.listen(port, () => {
+  console.log(`Application exemple à l'écoute sur le port ${port}!`)
+});
+
+ +

Les deux premières lignes importent (avec require()) le module express et créent une application express. Cet objet, qui est traditionnellement nommé app, possède des méthodes pour acheminer les requêtes HTTP, configurer les intergiciels, rendre les vues HTML, enregistrer un moteur de modèles et modifier les paramètres de l'application qui contrôlent le comportement de l'application (par exemple, le mode d'environnement, si les définitions de route sont sensibles à la casse, etc.).

+ +

La partie centrale du code (les trois lignes commençant par app.get) montre une définition de route. La méthode app.get() spécifie une fonction de rappel qui sera invoquée chaque fois qu'il y aura une requête HTTP GET avec un chemin ('/') relatif à la racine du site. La fonction de rappel prend une requête et un objet réponse comme arguments, et appelle simplement send() sur la réponse pour renvoyer la chaîne de caractères "Hello World!".

+ +

Le dernier bloc démarre le serveur sur le port 3000 et affiche un commentaire de journal dans la console. Avec le serveur en cours d'exécution, vous pouvez aller sur localhost:3000 dans votre navigateur pour voir l'exemple de réponse renvoyé.

+ +

Créer et importer des modules

+ +

Un module est une bibliothèque/fichier JavaScript que vous pouvez importer dans un autre code en utilisant la fonction require() de Node. Express lui-même est un module, tout comme les bibliothèques de middleware et de base de données que nous utilisons dans nos applications Express.

+ +

Le code ci-dessous montre comment nous importons un module par son nom, en utilisant le framework Express comme exemple. Tout d'abord, nous invoquons la fonction require(), en spécifiant le nom du module sous forme de chaîne ('express'), et en appelant l'objet retourné pour créer une applicationExpress. Nous pouvons alors accéder aux propriétés et fonctions de l'objet application.

+ +
const express = require('express');
+const app = express();
+
+ +

Vous pouvez également créer vos propres modules qui peuvent être importés de la même manière.

+ +
+

Note : Vous voudrez créer vos propres modules, car cela vous permet d'organiser votre code en parties maniables — une application monolithique à fichier unique est difficile à comprendre et à maintenir. L'utilisation de modules vous aide également à gérer votre espace de noms, car seules les variables que vous exportez explicitement sont importées lorsque vous utilisez un module.

+
+ +

Pour rendre les objets disponibles en dehors d'un module, il suffit de les affecter à l'objet exports. Par exemple, le module square.js ci-dessous est un fichier qui exporte les méthodes area() et perimeter() :

+ +
exports.area = function(width) { return width * width; };
+exports.perimeter = function(width) { return 4 * width; };
+ +

Nous pouvons importer ce module en utilisant require(), puis appeler la ou les méthodes exportées comme indiqué :

+ +
var square = require('./square'); // Ici, nous demandons le nom du fichier sans l'extension de fichier .js (facultative).
+console.log("L'aire d'un carré dont la largeur est de 4 est la suivante " + square.area(4));
+ +
+

Note : Vous pouvez également spécifier un chemin absolu vers le module (ou un nom, comme nous l'avons fait initialement).

+
+ +

Si vous souhaitez exporter un objet complet en une seule affectation au lieu de le construire une propriété à la fois, affectez-le à module.exports comme indiqué ci-dessous (vous pouvez également procéder ainsi pour faire de la racine de l'objet exports un constructeur ou une autre fonction) :

+ +
module.exports = {
+  area: function(width) {
+    return width * width;
+  },
+
+  perimeter: function(width) {
+    return 4 * width;
+  }
+};
+ +
+

Note : L'objet exports peut être vu comme un raccourci vers module.exports au sein d'un module donné. En fait, exports est simplement une variable qui est initialisée avec la valeur de module.exports avant que le module soit évalué. Cette valeur est une référence vers un objet. Cela signifie que exports référence le même objet que celui référencé par module.exports. Cela signifie également qu'affecter une autre valeur à exports le détachera complètement de module.exports.

+
+ + +

Pour de plus amples informations sur les modules, voir Modules (documentation Node).

+ +

Utilisation des API asynchrones

+ +

Le code JavaScript utilise fréquemment des API asynchrones plutôt que synchrones pour les opérations qui peuvent prendre un certain temps à se terminer. Une API synchrone est une API dans laquelle chaque opération doit être terminée avant que l'opération suivante puisse commencer. Par exemple, les fonctions d'enregistrement suivantes sont synchrones et impriment le texte dans la console dans l'ordre (Premier, Second).

+ +
console.log('Premier');
+console.log('Second');
+ +

En revanche, une API asynchrone est une API qui lance une opération et revient immédiatement (avant que l'opération ne soit terminée). Une fois l'opération terminée, l'API utilisera un mécanisme quelconque pour effectuer des opérations supplémentaires. Par exemple, le code ci-dessous imprimera « Second, Premier » car même si la méthode setTimeout() est appelée en premier, et revient immédiatement, l'opération ne se termine pas avant plusieurs secondes.

+ +
setTimeout(function() {
+  console.log('Premier');
+}, 3000);
+console.log('Second');
+ +

L'utilisation d'API asynchrones non bloquantes est encore plus importante sur Node que dans le navigateur, car Node est un environnement d'exécution événementiel avec un seul thread. Cela signifie que toutes les requêtes adressées au serveur sont exécutées sur le même thread (plutôt que d'être fractionnées en threads distincts). Ce modèle est extrêmement efficace en termes de vitesse et de ressources du serveur, mais il signifie que si l'une de vos fonctions appelle des méthodes synchrones qui prennent beaucoup de temps pour se terminer, elle bloquera non seulement la demande actuelle, mais aussi toutes les autres demandes traitées par votre application Web.

+ +

Il existe plusieurs façons pour une API asynchrone d'informer votre application de la fin d'une opération. La méthode la plus courante consiste à enregistrer une fonction de rappel lorsque vous invoquez l'API asynchrone, qui sera rappelée lorsque l'opération sera terminée. C'est l'approche utilisée ci-dessus.

+ +
+

Note : L'utilisation des rappels (« callbacks ») peut être assez « désordonnée » si vous avez une séquence d'opérations asynchrones dépendantes qui doivent être exécutées dans l'ordre, car cela entraîne de multiples niveaux de rappels imbriqués. Ce problème est communément appelé « l'enfer des callbacks ». Ce problème peut être réduit par de bonnes pratiques de codage dont l'utilisation des promesses ou de async/await.

+
+ +
+

Note : Une convention courante pour Node et Express est d'utiliser des callbacks de type error-first. Dans cette convention, la première valeur de vos fonctions de rappel est une valeur d'erreur, tandis que les arguments suivants contiennent des données de succès. Il y a une bonne explication de l'utilité de cette approche dans ce blog : The Node.js Way - Comprendre les callbacks de type « Error First ». (fredkschott.com).

+
+ +

Créer des gestionnaires de route

+ +

Dans notre exemple Hello World d'Express (voir ci-dessus), nous avons défini une fonction de gestion de route (un callback) pour les requêtes HTTP GET vers la racine du site ('/').

+ +
app.get('/', (req, res) => {
+  res.send('Hello World!');
+});
+ +

La fonction de rappel prend une requête et un objet réponse comme arguments. Dans ce cas, la méthode appelle simplement send() sur la réponse pour renvoyer la chaîne de caractères « Hello World ! ». Il existe un nombre d'autres méthodes de réponse pour terminer le cycle requête/réponse, par exemple vous pourriez appeler res.json() pour envoyer une réponse JSON ou res.sendFile() pour envoyer un fichier.

+ +
+

Note : Vous pouvez utiliser les noms d'arguments de votre choix dans les fonctions de rappel ; lorsque le rappel est invoqué, le premier argument sera toujours la requête et le second sera toujours la réponse. Il est judicieux de les nommer de telle sorte que vous puissiez identifier l'objet avec lequel vous travaillez dans le corps du callback.

+
+ +

L'objet Express application fournit également des méthodes permettant de définir des gestionnaires de route pour tous les autres verbes HTTP, qui sont pour la plupart utilisés exactement de la même manière :

+

checkout(), copy(), delete(), get(), head(), lock(), merge(), mkactivity(), mkcol(), move(), m-search(), notify(), options(), patch(), post(), purge(), put(), report(), search(), subscribe(), trace(), unlock(), unsubscribe().

+ +

Il existe une méthode de routage spéciale, app.all(), qui sera appelée en réponse à toute méthode HTTP. Ceci est utilisé pour charger les fonctions middleware à un chemin particulier pour toutes les méthodes de requête. L'exemple suivant (tiré de la documentation d'Express) montre un gestionnaire qui sera exécuté pour les requêtes vers /secret indépendamment du verbe HTTP utilisé (à condition qu'il soit supporté par le module http).

+ +
app.all('/secret', (req, res, next) => {
+  console.log('Accès à la section secrète ...');
+  next(); // passe le contrôle au gestionnaire suivant
+});
+ +

Les routes vous permettent de faire correspondre des modèles particuliers de caractères dans une URL, d'extraire certaines valeurs de l'URL et de les transmettre comme paramètres au gestionnaire de la route (en tant qu'attributs de l'objet de la demande transmis comme paramètre).

+ +

Il est souvent utile de regrouper les gestionnaires de route pour une partie particulière d'un site et d'y accéder en utilisant un préfixe de route commun (par exemple, un site avec un Wiki pourrait avoir toutes les routes liées au wiki dans un seul fichier et les faire accéder avec un préfixe de route de /wiki/). Dans Express, ceci est réalisé en utilisant l'objet express.Router. Par exemple, nous pouvons créer notre route wiki dans un module nommé wiki.js, puis exporter l'objet Router, comme indiqué ci-dessous :

+ +
// wiki.js - Module route du wiki
+
+const express = require('express');
+const router = express.Router();
+
+// Route vers la page d'accueil
+router.get('/', (req, res) => {
+  res.send("Page d'accueil du wiki");
+});
+
+
+// Route vers la page à propos
+router.get('/about', (req, res) => {
+  res.send('À propos de ce wiki');
+});
+
+module.exports = router;
+ +
+

Note : L'ajout de routes à l'objet Router est identique à l'ajout de routes à l'objet app (comme indiqué précédemment).

+
+ +

Pour utiliser le routeur dans notre fichier d'application principal, nous devrions alors require() le module route (wiki.js), puis appeler use() sur l'application Express pour ajouter le routeur au chemin de manipulation du middleware. Les deux routes seront alors accessibles depuis /wiki/ et /wiki/about/.

+ +
const wiki = require('./wiki.js');
+// ...
+app.use('/wiki', wiki);
+ +

Nous vous montrerons beaucoup plus en détails comment travailler avec les routes, et en particulier comment utiliser le Router, plus tard dans la section liée Routes et contrôleurs.

+ +

Utilisation d'un middleware/intergiciel

+ +

L'intergiciel (aussi appelé « middleware ») est largement utilisé dans les applications d'Express, pour des tâches allant du service de fichiers statiques à la gestion des erreurs, en passant par la compression des réponses HTTP. Alors que les fonctions de route terminent le cycle requête-réponse HTTP en renvoyant une réponse au client HTTP, les fonctions d'intergiciel effectuent typiquement une opération sur la demande ou la réponse, puis appellent la fonction suivante dans la « pile », qui peut être un autre intergiciel ou un gestionnaire de route. L'ordre dans lequel les intergiciels sont appelés dépend du code de l'application.

+ +
+

Note : L'intergiciel peut effectuer n'importe quelle opération, exécuter n'importe quel code, apporter des modifications à l'objet requête et réponse, et il peut aussi mettre fin au cycle requête-réponse. S'il ne met pas fin au cycle, alors il doit appeler next() pour passer le contrôle à la fonction suivante de l'intergiciel (ou la requête sera laissée en suspens).

+
+ +

La plupart des apps utiliseront des programmes intermédiaires tiers afin de simplifier les tâches courantes de développement web comme le travail avec les cookies, les sessions, l'authentification des utilisateurs, l'accès aux données POST et JSON des requêtes, la journalisation, etc. Vous pouvez trouver une liste des paquets middleware maintenus par l'équipe Express (qui inclut également d'autres paquets populaires de tiers). D'autres paquets Express sont disponibles sur le gestionnaire de paquets NPM.

+ +

Pour utiliser un middleware tiers, vous devez d'abord l'installer dans votre application à l'aide de NPM. Par exemple, pour installer l'intergiciel de journalisation des requêtes HTTP morgan, vous devez procéder comme suit :

+ +
npm install morgan
+ +

Vous pourriez alors appeler use() sur l'objet Express application pour ajouter l'intergiciel à la pile :

+ +
const express = require('express');
+const logger = require('morgan');
+const app = express();
+app.use(logger('dev'));
+...
+ +
+

Note : Les fonctions d'intergiciel et de routage sont appelées dans l'ordre où elles sont déclarées. Pour certains intergiciels, l'ordre est important (par exemple, si l'intergiciel de session dépend de l'intergiciel de cookie, alors le gestionnaire de cookie doit être ajouté en premier). Il est presque toujours nécessaire d'appeler l'intergiciel avant de définir les routes, sinon vos gestionnaires de routes n'auront pas accès aux fonctionnalités ajoutées par votre intergiciel.

+
+ +

Vous pouvez écrire vos propres fonctions intergiciels, et vous serez probablement amené à le faire (ne serait-ce que pour créer un code de gestion des erreurs). La seule différence entre une fonction middleware et un callback de gestionnaire de route est que les fonctions middleware ont un troisième argument next, que les fonctions middleware sont censées appeler si elles ne sont pas celle qui termine le cycle de requête (lorsque la fonction middleware est appelée, cela contient la fonction next qui doit être appelée).

+ +

Vous pouvez ajouter une fonction d'intergiciel à la chaîne de traitement avec app.use() ou app.add(), selon que vous voulez appliquer l'intergiciel à toutes les réponses ou aux réponses avec un verbe HTTP particulier (GET, POST, etc). Vous spécifiez les routes de la même manière dans les deux cas, bien que la route soit facultative lors de l'appel à app.use().

+ +

L'exemple ci-dessous montre comment vous pouvez ajouter la fonction middleware en utilisant les deux méthodes, et avec/sans route.

+ +
const express = require('express');
+const app = express();
+
+// Un exemple de fonction middleware
+let a_middleware_function = function(req, res, next) {
+  // ... effectuer certaines opérations
+  next(); // Appelez next() pour qu'Express appelle la fonction middleware suivante dans la chaîne.
+}
+
+// Fonction ajoutée avec use() pour toutes les routes et verbes
+app.use(a_middleware_function);
+
+// Fonction ajoutée avec use() pour une route spécifique
+app.use('/uneroute', a_middleware_function);
+
+// Une fonction middleware ajoutée pour un verbe et une route HTTP spécifiques
+app.get('/', a_middleware_function);
+
+app.listen(3000);
+ +
+

Note : Ci-dessus, nous déclarons la fonction middleware séparément, puis nous la définissons comme fonction de rappel. Dans notre précédente fonction de gestion de route, nous avons déclaré la fonction de rappel lorsqu'elle a été utilisée. En JavaScript, les deux approches sont valables.

+
+ +

La documentation d'Express contient beaucoup d'autres excellents documents sur l'utilisation et l'écriture d'intergiciels Express.

+ +

Servir les fichiers statiques

+ +

Vous pouvez utiliser l'intergiciel express.static pour servir des fichiers statiques, notamment vos images, CSS et JavaScript (static() est la seule fonction de l'intergiciel qui fait réellement partie d'Express). Par exemple, vous utiliserez la ligne ci-dessous pour servir des images, des fichiers CSS et des fichiers JavaScript à partir d'un répertoire nommé 'public' au même niveau que celui où vous appelez node :

+ +
app.use(express.static('public'));
+ +

Tous les fichiers du répertoire public sont servis en ajoutant leur nom de fichier (relatif au répertoire "public" de base) à l'URL de base. Ainsi, par exemple :

+ +
https://localhost:3000/images/dog.jpg
+https://localhost:3000/css/style.css
+https://localhost:3000/js/app.js
+https://localhost:3000/about.html
+ +

Vous pouvez appeler static() plusieurs fois pour servir plusieurs répertoires. Si un fichier ne peut pas être trouvé par une fonction middleware, alors il sera simplement transmis au middleware suivant (l'ordre dans lequel le middleware est appelé est basé sur votre ordre de déclaration).

+ +
app.use(express.static('public'));
+app.use(express.static('media'));
+ +

Vous pouvez également créer un préfixe virtuel pour vos URL statiques, plutôt que de voir les fichiers ajoutés à l'URL de base. Par exemple, ici nous spécifions un chemin de montage pour que les fichiers soient chargés avec le préfixe « /media » :

+ +
app.use('/media', express.static('public'));
+ +

Maintenant, vous pouvez charger les fichiers qui se trouvent dans le répertoire public à partir du préfixe du chemin /media.

+ +
https://localhost:3000/media/images/dog.jpg
+https://localhost:3000/media/video/cat.mp4
+https://localhost:3000/media/cry.mp3
+ +
+

Note : Voir également Servir des fichiers statiques dans Express.

+
+ +

Traitement des erreurs

+ +

Les erreurs sont traitées par une ou plusieurs fonctions spéciales du middleware qui ont quatre arguments, au lieu des trois habituels : (err, req, res, next). Par exemple :

+ +
app.use(function(err, req, res, next) {
+  console.error(err.stack);
+  res.status(500).send("Quelque chose s'est cassé !");
+});
+ +

Ceux-ci peuvent retourner tout contenu nécessaire, mais doivent être appelés après tous les autres app.use() et les appels de routes afin qu'ils soient le dernier middleware dans le processus de traitement des requêtes !

+ +

Express est livré avec un gestionnaire d'erreurs intégré, qui prend en charge toutes les erreurs restantes qui pourraient être rencontrées dans l'application. Cette fonction middleware de gestion des erreurs par défaut est ajoutée à la fin de la pile de fonctions middleware. Si vous passez une erreur à next() et que vous ne la gérez pas dans un gestionnaire d'erreurs, elle sera traitée par le gestionnaire d'erreurs intégré ; l'erreur sera écrite au client avec la trace de la pile.

+ +
+

Note : La trace de la pile n'est pas incluse dans l'environnement de production. Pour exécuter une application serveur Express, la variable d'environnement NODE_ENV doit être définie avec la valeur production.

+
+ +
+

Note : Les codes d'état HTTP 404 et autres « erreurs » ne sont pas traités comme des erreurs. Si vous voulez les gérer, vous pouvez ajouter une fonction middleware pour le faire. Pour plus d'informations, consultez la FAQ.

+
+ +

Pour plus d'informations, voir Gestion des erreurs (docs Express).

+ +

Utilisation des bases de données

+ +

Les apps Express peuvent utiliser tout mécanisme de base de données pris en charge par Node (Express lui-même ne définit aucun comportements/exigences supplémentaire spécifique pour la gestion des bases de données). Il existe de nombreuses options, notamment PostgreSQL, MySQL, Redis, SQLite, MongoDB, etc.

+ +

Pour les utiliser, vous devez d'abord installer le pilote de base de données à l'aide de NPM. Par exemple, pour installer le pilote de la populaire base de données NoSQL MongoDB, vous devez utiliser la commande suivante :

+ +
$ npm install mongodb
+ +

La base de données elle-même peut être installée localement ou sur un serveur en nuage. Dans votre code Express, vous avez besoin du pilote, vous vous connectez à la base de données, puis vous effectuez des opérations de création, lecture, mise à jour et suppression (en anglais, on utilise l'acronyme CRUD qui signifie Create, Read, Update, Delete). L'exemple ci-dessous (tiré de la documentation d'Express) montre comment vous pouvez trouver des enregistrements « mammifères » en utilisant MongoDB.

+ +
// cela fonctionne avec les anciennes versions de mongodb version ~ 2.2.33
+const MongoClient = require('mongodb').MongoClient;
+
+MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
+  if (err) throw err;
+
+  db.collection('mammals').find().toArray(function (err, result) {
+    if (err) throw err;
+
+    console.log(result);
+  });
+});
+
+// pour mongodb version 3.0 et supérieure
+const MongoClient = require('mongodb').MongoClient;
+MongoClient.connect('mongodb://localhost:27017/animals', function(err, client){
+   if(err) throw err;
+
+   let db = client.db('animals');
+   db.collection('mammals').find().toArray(function(err, result){
+     if(err) throw err;
+     console.log(result);
+     client.close();
+   });
+});
+ +

Une autre approche populaire consiste à accéder à votre base de données de manière indirecte, via un mappeur objet-relationnel (« ORM »). Dans cette approche, vous définissez vos données en tant qu'objets ou modèles et l'ORM les met en correspondance avec le format de base de données sous-jacent. L'avantage de cette approche est qu'en tant que développeur, vous pouvez continuer à penser en termes d'objets JavaScript plutôt qu'en termes de sémantique de base de données, et qu'il existe un endroit évident pour effectuer la validation et la vérification des données entrantes. Nous parlerons davantage des bases de données dans un article ultérieur.

+ +

Pour plus d'informations, voir Intégration de base de données (docs Express).

+ +

Rendu des données (vues)

+ +

Les moteurs de modèles (appelés « moteurs de vue » par Express) vous permettent de spécifier la structure d'un document de sortie dans un modèle, en utilisant des espaces réservés pour les données qui seront remplies lorsqu'une page sera générée. Les modèles sont souvent utilisés pour créer du HTML, mais peuvent également créer d'autres types de documents. Express prend en charge un certain nombre de moteurs de modèles, et il existe une comparaison utile des moteurs les plus populaires ici : Comparaison des moteurs de création de modèles JavaScript : Jade, Mustache, Dust et plus.

+ +

Dans le code des paramètres de votre application, vous définissez le moteur de modèles à utiliser et l'emplacement où Express doit rechercher les modèles à l'aide des paramètres « views » et « view engines », comme indiqué ci-dessous (vous devrez également installer le paquet contenant votre bibliothèque de modèles !)

+ +
const express = require('express');
+const path = require('path');
+const app = express();
+
+// Définir le répertoire contenant les modèles ('views')
+app.set('views', path.join(__dirname, 'views'));
+
+// Définir le moteur d'affichage à utiliser, dans ce cas 'some_template_engine_name'.
+app.set('view engine', 'some_template_engine_name');
+ +

L'apparence du modèle dépendra du moteur que vous utilisez. En supposant que vous ayez un fichier de modèle nommé « index.<template_extension> » qui contient des espaces réservés pour des variables de données nommées « title » et « message », vous appelleriez Response.render() dans une fonction de gestionnaire de route pour créer et envoyer la réponse HTML :

+ +
app.get('/', function(req, res) {
+  res.render('index', { title: 'À propos des poules', message: 'Elles sont où ?' });
+});
+ +

Pour plus d'informations, voir Utilisation des moteurs de modèles avec Express (docs Express).

+ +

Structure du fichier

+ +

Express ne fait aucune supposition en termes de structure ou de composants que vous utilisez. Les routes, les vues, les fichiers statiques et toute autre logique spécifique à l'application peuvent vivre dans un nombre quelconque de fichiers avec n'importe quelle structure de répertoire. Bien qu'il soit parfaitement possible d'avoir l'ensemble de l'application Express dans un seul fichier, il est généralement judicieux de diviser votre application en fichiers basés sur la fonction (par exemple, gestion de compte, blogs, forums de discussion) et le domaine de problème architectural (par exemple, modèle, vue ou contrôleur si vous utilisez une architecture MVC).

+ +

Dans une prochaine rubrique, nous utiliserons le Générateur d'applications express, qui crée un squelette d'application modulaire que nous pouvons facilement étendre pour créer des applications web.

+ +

Résumé

+ +

Félicitations, vous avez terminé la première étape de votre voyage Express/Node ! Vous devriez maintenant comprendre les principaux avantages d'Express et de Node, et savoir à quoi ressemblent les principales parties d'une application Express (routes, intergiciels, gestion des erreurs et code modèle). Vous devez également comprendre qu'Express étant un framework non autonome, la manière dont vous assemblez ces éléments et les bibliothèques que vous utilisez dépendent largement de vous !

+ +

Bien sûr, Express est délibérément un cadre d'application web très léger, et une grande partie de ses avantages et de son potentiel provient de bibliothèques et de fonctionnalités tierces. Nous les examinerons plus en détail dans les articles suivants. Dans notre prochain article, nous nous pencherons sur la configuration d'un environnement de développement Node, afin que vous puissiez commencer à voir du code Express en action.

+ +

Voir aussi

+ + + +
{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}
+ +

Dans ce module

+ + diff --git a/files/fr/learn/server-side/first_steps/client-server_overview/index.html b/files/fr/learn/server-side/first_steps/client-server_overview/index.html deleted file mode 100644 index 9323086beb..0000000000 --- a/files/fr/learn/server-side/first_steps/client-server_overview/index.html +++ /dev/null @@ -1,317 +0,0 @@ ---- -title: La relation Client-Serveur -slug: Learn/Server-side/First_steps/Client-Server_overview -translation_of: Learn/Server-side/First_steps/Client-Server_overview -original_slug: Learn/Server-side/Premiers_pas/Client-Serveur ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}
- -

Maintenant que vous connaissez le but et le bénéfice de la programmation côté serveur, nous allons analyser en détails ce qui se passe quand un serveur reçoit une requête dynamique de la part d'un navigateur. Comme la plupart des sites gèrent le code côté serveur (requêtes et réponses) de la même manière, cela vous aidera à comprendre ce que vous devrez faire ensuite en écrivant votre propre code.

- - - - - - - - - - - - -
Prérequis :Compréhension basique des notions informatiques et de ce qu'est un serveur web.
Objectif :Comprendre les interactions client-serveur sur un site dynamique et particulièrement quelles opérations devront être effectuées par le code côté serveur.
- -

Il n'y a pas de code "réel" dans la suite de cette présentation parce que nous n'avons pas encore choisi un framework web à utiliser pour écrire notre code ! Ce tutoriel est quand même trés pertinent car les comportements décrits doivent être implémentés par votre code côté serveur, sans qu'il ait à se soucier (le serveur...)   de quel langage de programmation ou framework vous vous servez.

- -

Serveurs Web et HTTP (un avant-goût)

- -

Les navigateurs web communiquent avec les serveurs web avec le protocole HTTP : HyperTextTransfer Protocol. Quand vous cliquez un lien sur une page, soumettez un formulaire ou lancez une recherche, le navigateur envoie une requête HTTP (HTTP Request) au serveur.

- -

Cette requête inclut :

- - - -

Les serveurs Web attendent une requête du client puis la traitent quand elle arrive. Il répond ensuite au navigateur avec un message HTTP Response. La réponse contient un statut HTTP Response indiquant si, oui ou non, la requête a abouti. (ex : "200 OK" pour un succés, "404 Not Found" si la ressource ne peut être trouvée, "403 Forbidden" si l'utilisateur n'est pas autorisé à voir la ressource etc. Le corps d'une réponse aboutie à une requête  GET contiendrait la ressource demandée.

- -

Quand une page HTML est retournée, elle est affichée par le navigateur. Le navigateur, nativement, pourra découvrir des liens vers d'autres ressources (ex : une page HTML intégre habituellement des pages JavaScript et CSS ), et enverra des requêtes séparées pour télécharger ces fichiers.

- -

Les sites web dynamiques ou statiques (voir sections suivantes) utilisent les mêmes protocoles/modèles de communication.

- -

Exemple de requête/réponse GET 

- -

Vous faites une simple requête GET en cliquant sur un lien ou en faisant une recherche sur un site (sur une page de moteur de recherche par exemple). Une requête HTTP envoyée lorsque vous effectuez une recherche sur MDN pour les termes : "La relation Client-Serveur" ressemblera beaucoup à ce qui suit mais ne sera pas identique car des parties du message dépendent des paramètres de votre navigateur.

- -
-

Note : Le format des messsages HTTP est défini par un standard web  (RFC7230). Vous n'avez pas besoin de connaître ce niveau de détails mais vous saurez au moins d'où vient tout ça !

-
- -

La requête

- -

Chaque ligne de la requête contient des informations sur celle-ci. La première partie est appelée l'en-tête ( header) et contient beaucoup de données utiles. De la même manière qu'un HTML head contient des informations utiles (pas le contenu réel qui lui, se trouve dans le corps (body) :

- -
GET https://developer.mozilla.org/en-US/search?q=la+relation+Client+-+serveur&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1
-Host: developer.mozilla.org
-Connection: keep-alive
-Pragma: no-cache
-Cache-Control: no-cache
-Upgrade-Insecure-Requests: 1
-User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
-Referer: https://developer.mozilla.org/en-US/
-Accept-Encoding: gzip, deflate, sdch, br
-Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7
-Accept-Language: en-US,en;q=0.8,es;q=0.6
-Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _gat=1; _ga=GA1.2.1688886003.1471911953; ffo=true
-
- -

Les premières et secondes lignes contiennent la plupart des données déjà évoquées précédemment :

- - - -

La dernière ligne contient des données sur les cookies côté client — vous observerez que dans ce cas, le cookie a une id pour gérer la session :    (Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; ...).

- -

Les lignes restantes concernent le navigateur utilisé et les sortes de réponses qu'il peut accepter. Par exemple, vous pouvez voir ceci :

- - - -

Les requêtes HTTP peuvent aussi avoir un corps mais dans ce cas précis, il est vide.

- -

La réponse

- -

La première partie de la réponse à cette requête est détaillée ci-dessous. L'en-tête contient les données suivantes :

- - - -

À la fin du message nous avons le contenu du corps  — lequel contient le "vrai" HTML demandé par la requête.

- -
HTTP/1.1 200 OK
-Server: Apache
-X-Backend-Server: developer1.webapp.scl3.mozilla.com
-Vary: Accept,Cookie, Accept-Encoding
-Content-Type: text/html; charset=utf-8
-Date: Wed, 07 Sep 2016 00:11:31 GMT
-Keep-Alive: timeout=5, max=999
-Connection: Keep-Alive
-X-Frame-Options: DENY
-Allow: GET
-X-Cache-Info: caching
-Content-Length: 41823
-
-
-
-<!DOCTYPE html>
-<html lang="en-US" dir="ltr" class="redesign no-js"  data-ffo-opensanslight=false data-ffo-opensans=false >
-<head prefix="og: http://ogp.me/ns#">
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
-  <script>(function(d) { d.className = d.className.replace(/\bno-js/, ''); })(document.documentElement);</script>
-  ...
-
- -

Le reste de l'en-tête de la réponse contient des informations sur la réponse elle-même (quand elle a été générée), sur le serveur et comment le navigateur doit gérer la page ( X-Frame-Options: DENY cette ligne dit au navigateur de ne pas autoriser cette page a être intégrée dans une  {{htmlelement("iframe")}} dans un autre site).

- -

Exemple de requête/réponse POST

- -

Un POST HTTP est effectué lorsque vous soumettez un formulaire contenant des données à sauvegarder sur le serveur.

- -

La requête

- -

Le texte ci-dessous montre une requête HTTP faite quand un utlisateur soumet un nouveaux profil sur ce site. Le format de la requête est presque le même que celui de la requête GET vue précédemment, bien que la première ligne identifie cette requête comme un POST

- -
POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1
-Host: developer.mozilla.org
-Connection: keep-alive
-Content-Length: 432
-Pragma: no-cache
-Cache-Control: no-cache
-Origin: https://developer.mozilla.org
-Upgrade-Insecure-Requests: 1
-User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
-Content-Type: application/x-www-form-urlencoded
-Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
-Referer: https://developer.mozilla.org/en-US/profiles/hamishwillee/edit
-Accept-Encoding: gzip, deflate, br
-Accept-Language: en-US,en;q=0.8,es;q=0.6
-Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; _gat=1; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _ga=GA1.2.1688886003.1471911953; ffo=true
-
-csrfmiddlewaretoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT&user-username=hamishwillee&user-fullname=Hamish+Willee&user-title=&user-organization=&user-location=Australia&user-locale=en-US&user-timezone=Australia%2FMelbourne&user-irc_nickname=&user-interests=&user-expertise=&user-twitter_url=&user-stackoverflow_url=&user-linkedin_url=&user-mozillians_url=&user-facebook_url=
- -

La principale différence est que l'URL ne comporte pas de paramètres.  Comme vous voyez, l'information du formulaire est encodée dans le corps de la requête (par exemple : le nom complet du nouvel utilisateur est paramétré avec  &user-fullname=Hamish+Willee).

- -

La réponse

- -

La réponse à la requête est expliquée dessous. Le statut "302 Found" dit au navigateur que le post a abouti et qu'il peut délivrer une deuxième requête HTTP pour charger la page spécifiée dans le champ  Location. L'information est donc en cela similaire à une réponse de requête GET.

- -
HTTP/1.1 302 FOUND
-Server: Apache
-X-Backend-Server: developer3.webapp.scl3.mozilla.com
-Vary: Cookie
-Vary: Accept-Encoding
-Content-Type: text/html; charset=utf-8
-Date: Wed, 07 Sep 2016 00:38:13 GMT
-Location: https://developer.mozilla.org/en-US/profiles/hamishwillee
-Keep-Alive: timeout=5, max=1000
-Connection: Keep-Alive
-X-Frame-Options: DENY
-X-Cache-Info: not cacheable; request wasn't a GET or HEAD
-Content-Length: 0
-
- -
-

Note : Les requêtes et réponses montrées dans ces exemples ont été capturées avec l'application Fiddler , mais vous pouvez avoir des informations similaires en utilisant des "renifleurs" web  (e.g. Websniffer, Wireshark) ou des extensions de navigateur comme  HttpFox. Vous pouvez essayer seul. Utilisez tous les outils recommandés, naviguez sur des sites et  éditez des profils de données pour explorer les différentes requêtes et réponses. La plupart des navigateurs modernes ont aussi des outils qui gérent les requêtes réseau, par exemple le Network Monitor dans Firefox).

-
- -

Les sites statiques

- -

Un site statique renvoie le même contenu codé en dur depuis le serveur quelle que soit la ressource demandée. Si vous avez une page concernant un produit à l'adresse  /static/myproduct1.html, cette même page sera retournée à chaque utilisateur. Si vous ajoutez un nouveau produit, vous devez ajouter une nouvelle page (par ex : myproduct2.html) et ainsi de suite. Cela peut être vraiment inefficace — Comment faire quand vous avez des milliers de pages "produit" à faire ? Vous allez répéter beaucoup de code identique dans chaque page (le modèle de base de la page, sa structure, etc.) et si vous voulez changer quoique ce soit dans la structure de la page — comme une section "produits dérivés" par exemple — alors, il faudra changer chaque page individuellement..

- -
-

Note : Les sites statiques sont trés efficace quand vous avez un petit nombre de pages et que vous voulez envoyer le même contenu à chaque utilisateur. De toutes façons, ils peuvent avoir un coût certain de maintenance au fur et à mesure de l'augmentation du nombre de pages.

-
- -

Voyons comment tout cela marche en révisant un diagramme d'architecture de site statique vu dans l'article précédent.

- -

A simplified diagram of a static web server.

- -

Quand un utilisateur veut naviguer jusqu'à une page, le navigateur envoie une requête HTTP GET spécifiant l'URL de sa page HTML. Le serveur retourne le document demandé depuis son système de fichiers et retourne une réponse  HTTP contenant le document et un  HTTP Response status code ( statut codé de la réponse HTTP) qui est  "200 OK" (indiquant le succés de l'opération). Le serveur peut retourner un statut différent, par exemple "404 Not Found"  si le fichier est absent sur le serveur , ou bien "301 Moved Permanently" si le fichier existe mais a été déplacé vers une nouvelle localisation.

- -

Le serveur d'un site statique n'aura à faire face qu'à des requêtes  GET vu qu'il ne stocke aucune donnée modifiable. Il ne change pas non plus ses réponses basées sur les données des requêtes HTTP (c'est à dire les paramètres URL ou les cookies). 

- -

Comprendre comment fonctionnent les sites statiques est sans aucun doute trés utile à l'apprentissage de la programmation côté serveur car les sites dynamiques gèrent les requêtes pour les fichiers statiques (CSS, JavaScript, images statiques , etc.) exactement de la même manière.

- -

Les sites dynamiques

- -

Un site dynamique peut générer et retourner du contenu basé sur une requête URL spécifique et les données (plutôt que de toujours renvoyer le même fichier codé en dur à une URL particulière).  Toujours avec l'exemple d'un site "produits", le serveur stockera les données du produit dans une base de données plutôt que dans un fichier HTML individuel. Quand il reçoit une requête HTTP GET pour un produit, le serveur détermine l'ID du produit, va chercher les données dans la base de données puis construit la page HTML pour la réponse en intégrant les données dans un gabarit (template) HTML. C'est un avantage indéniable sur un site statique :

- -

Utiliser une base de données permet à l'information "produit" d'être stockée efficacement, en étant modifiable, extensible et bien indexée.

- -

Employer des gabarits HTML facilite la façon de changer la structure HTML parce que c'est fait en un seul endroit, dans un seul gabarit (template) et non pas sur potentiellement des milliers de pages statiques.

- -

Anatomie d'un requête dynamique

- -

Cette section présente une vue d'ensemble du cycle dynamique HTTP de requête/réponse, construit avec ce que nous avons vu précédemment avec de plus amples détails. Toujours dans l'optique de "faire les choses en réel" nous utiliserons le contexte du site d'une équipe de sport où l'entraîneur peut sélectionner le nom de l'équipe et le nombre de joueurs dans un formulaire HTML et avoir en retour une suggestion "Meilleure composition" pour le prochain match.

- -

Le diagramme ci-dessous montre les principaux éléments du site Web "entraîneur d'équipe", ainsi que des étiquettes numérotées pour la séquence des opérations lorsque l'entraîneur accède à la liste "meilleure équipe". Les parties du site qui le rendent dynamique sont l’application Web (c’est ainsi que nous nous référerons au code côté serveur qui traite les requêtes HTTP et renvoie les réponses HTTP), la base de données, qui contient des informations sur les joueurs, les équipes, les entraîneurs et leurs partenaires. relations, et les modèles HTML.

- -

This is a diagram of a simple web server with step numbers for each of step of the client-server interaction.

- -

Une fois que l’entraîneur a soumis le formulaire avec le nom de l’équipe et le nombre de joueurs, la séquence des opérations est la suivante:

- -
    -
  1. Le navigateur Web crée une requête HTTP GET au serveur en utilisant l’URL de base de la ressource (/ best) et en codant l’équipe et le numéro du joueur sous forme de paramètres d’URL (par exemple / best? team=my_team_name&show = 11) ou dans le cadre de l’URL modèle (par exemple / best / my_team_name / 11 /). Une requête GET est utilisée car la requête extrait uniquement des données (sans les modifier).
  2. -
  3. Le serveur Web détecte que la demande est "dynamique" et la transmet à l'application Web pour traitement (le serveur Web détermine comment gérer différentes URL en fonction des règles de correspondance de modèle définies dans sa configuration).
  4. -
  5. L'application Web identifie l'objectif de la demande d'obtenir la "meilleure liste d'équipes" en fonction de l'URL (/ best /) et recherche le nom d'équipe requis et le nombre de joueurs à partir de l'URL. L'application Web obtient alors les informations requises de la base de données (en utilisant des paramètres "internes" supplémentaires pour définir quels joueurs sont les "meilleurs", et éventuellement en obtenant également l'identité de l'entraîneur connecté à partir d'un cookie côté client).
  6. -
  7. L'application Web crée dynamiquement une page HTML en plaçant les données (de la base de données) dans des espaces réservés dans un modèle HTML.
  8. -
  9. L'application Web renvoie le code HTML généré au navigateur Web (via le serveur Web), ainsi qu'un code d'état HTTP de 200 ("success"). Si quoi que ce soit empêche le code HTML d'être renvoyé, l'application Web renvoie un autre code, par exemple "404" pour indiquer que l'équipe n'existe pas.
  10. -
  11. Le navigateur Web commence alors à traiter le code HTML renvoyé, en envoyant des demandes distinctes pour obtenir tous les fichiers CSS ou JavaScript qu’il référence (voir étape 7).
  12. -
  13. Le serveur Web charge les fichiers statiques à partir du système de fichiers et les renvoie directement au navigateur (là encore, le traitement correct des fichiers est basé sur les règles de configuration et la correspondance des types d'URL).
  14. -
- -

Une opération de mise à jour d'un enregistrement dans la base de données serait gérée de la même manière, sauf que, comme toute mise à jour de base de données, la demande HTTP du navigateur devrait être codée en tant que demande POST.

- -

que faire d'autre?

- -

Le travail d'une application Web consiste à recevoir des requêtes HTTP et à renvoyer des réponses HTTP. Bien que l'interaction avec une base de données pour obtenir ou mettre à jour des informations soit une tâche très courante, le code peut faire d'autres choses en même temps, ou ne pas interagir du tout avec une base de données.Un bon exemple de tâche supplémentaire qu'une application Web pourrait exécuter serait l'envoi d'un courrier électronique aux utilisateurs pour confirmer leur inscription sur le site. Le site peut également effectuer une journalisation ou d’autres opérations.

- -

Renvoyer autre chose que  du HTML

- -

Le code de site Web côté serveur ne doit pas nécessairement renvoyer des extraits / fichiers HTML dans la réponse. Au lieu de cela, il peut créer et renvoyer de manière dynamique d'autres types de fichiers (texte, PDF, CSV, etc.) ou même des données (JSON, XML, etc.).L'idée de renvoyer des données à un navigateur Web afin qu'il puisse mettre à jour de manière dynamique son propre contenu ({{glossary ("AJAX")}}) existe depuis un certain temps. Plus récemment, les "applications à page unique" sont devenues populaires, le site Web entier étant écrit avec un seul fichier HTML mis à jour de manière dynamique en cas de besoin. Les sites Web créés à l'aide de ce style d'application génèrent des coûts de calcul considérables entre le serveur et le navigateur Web, ce qui peut donner l'impression que les sites Web se comportent beaucoup plus comme des applications natives (très réactives, etc.).

- -

Les frameworks Web simplifient la programmation Web côté serveur

- -

Les infrastructures Web côté serveur facilitent beaucoup la rédaction de code permettant de gérer les opérations décrites ci-dessus.L’une des opérations les plus importantes qu’ils effectuent consiste à fournir des mécanismes simples pour mapper les URL de différentes ressources / pages à des fonctions de gestionnaire spécifiques. Cela facilite la séparation du code associé à chaque type de ressource. Cela présente également des avantages en termes de maintenance, car vous pouvez modifier l'URL utilisée pour fournir une fonctionnalité particulière à un endroit, sans avoir à changer la fonction de gestionnaire.Par exemple, considérons le code Django (Python) suivant qui mappe deux modèles d'URL à deux fonctions d'affichage. Le premier modèle garantit qu'une requête HTTP avec une URL de ressource / best sera transmise à une fonction nommée index () dans le module views. Une demande qui a pour motif "/ best / junior" sera plutôt transmise à la fonction d'affichage junior ().

- -
# file: best/urls.py
-#
-
-from django.conf.urls import url
-
-from . import views
-
-urlpatterns = [
-    # example: /best/
-    url(r'^$', views.index),
-    # example: /best/junior/
-    url(r'^junior/$', views.junior),
-]
- -
-

Note : Les premiers paramètres des fonctions url () peuvent paraître un peu bizarres (par exemple, r '^ junior / $') car ils utilisent une technique de correspondance de modèle appelée "expressions régulières" (RegEx ou RE). Vous n'avez pas besoin de savoir comment fonctionnent les expressions régulières à ce stade, car elles nous permettent également de faire correspondre les modèles de l'URL (plutôt que les valeurs codées en dur ci-dessus) et de les utiliser comme paramètres dans nos fonctions d'affichage. À titre d'exemple, un RegEx très simple pourrait dire "faire correspondre une seule lettre majuscule, suivie de 4 à 7 lettres minuscules".

-
- -
L'infrastructure Web permet également à une fonction d'affichage d'extraire facilement des informations de la base de données. La structure de nos données est définie dans des modèles, qui sont des classes Python qui définissent les champs à stocker dans la base de données sous-jacente. Si nous avons un modèle nommé Team avec un champ "team_type", nous pouvons utiliser une syntaxe de requête simple pour récupérer toutes les équipes ayant un type particulier.
- -
L’exemple ci-dessous donne la liste de toutes les équipes ayant le type d’équipe exact (sensible à la casse) de "junior" - notez le format: nom du champ (team_type) suivi du  double underscore, puis du type de match à utiliser (ici nous utilisons: exact). ). Il existe de nombreux autres types de match et nous pouvons les enchaîner. Nous pouvons également contrôler l'ordre et le nombre de résultats retournés.
- -
- -
#best/views.py
-
-from django.shortcuts import render
-
-from .models import Team
-
-
-def junior(request):
-    list_teams = Team.objects.filter(team_type__exact="junior")
-    context = {'list': list_teams}
-    return render(request, 'best/index.html', context)
-
- -

Une fois que la fonction junior () a obtenu la liste des équipes juniors, elle appelle la fonction render () en transmettant la requête HttpRequest d'origine, un modèle HTML et un objet "context" définissant les informations à inclure dans le modèle. La fonction render () est une fonction pratique qui génère du HTML à l'aide d'un context et d'un template HTML, puis le renvoie dans un objet HttpResponse.De toute évidence, les frameworks Web peuvent vous aider dans de nombreuses autres tâches. Nous discutons beaucoup plus d'avantages et de choix de frameworks Web populaires dans le prochain article.

- -

Summary

- -

À ce stade, vous devez avoir une bonne vue d'ensemble des opérations que le code côté serveur doit effectuer et connaître certaines des manières dont une infrastructure Web côté serveur peut faciliter cela.

- -

Dans un module suivant, nous vous aiderons à choisir le meilleur framework Web pour votre premier site.

- -

{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}

- -

In this module

- - diff --git a/files/fr/learn/server-side/first_steps/client-server_overview/index.md b/files/fr/learn/server-side/first_steps/client-server_overview/index.md new file mode 100644 index 0000000000..9323086beb --- /dev/null +++ b/files/fr/learn/server-side/first_steps/client-server_overview/index.md @@ -0,0 +1,317 @@ +--- +title: La relation Client-Serveur +slug: Learn/Server-side/First_steps/Client-Server_overview +translation_of: Learn/Server-side/First_steps/Client-Server_overview +original_slug: Learn/Server-side/Premiers_pas/Client-Serveur +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}
+ +

Maintenant que vous connaissez le but et le bénéfice de la programmation côté serveur, nous allons analyser en détails ce qui se passe quand un serveur reçoit une requête dynamique de la part d'un navigateur. Comme la plupart des sites gèrent le code côté serveur (requêtes et réponses) de la même manière, cela vous aidera à comprendre ce que vous devrez faire ensuite en écrivant votre propre code.

+ + + + + + + + + + + + +
Prérequis :Compréhension basique des notions informatiques et de ce qu'est un serveur web.
Objectif :Comprendre les interactions client-serveur sur un site dynamique et particulièrement quelles opérations devront être effectuées par le code côté serveur.
+ +

Il n'y a pas de code "réel" dans la suite de cette présentation parce que nous n'avons pas encore choisi un framework web à utiliser pour écrire notre code ! Ce tutoriel est quand même trés pertinent car les comportements décrits doivent être implémentés par votre code côté serveur, sans qu'il ait à se soucier (le serveur...)   de quel langage de programmation ou framework vous vous servez.

+ +

Serveurs Web et HTTP (un avant-goût)

+ +

Les navigateurs web communiquent avec les serveurs web avec le protocole HTTP : HyperTextTransfer Protocol. Quand vous cliquez un lien sur une page, soumettez un formulaire ou lancez une recherche, le navigateur envoie une requête HTTP (HTTP Request) au serveur.

+ +

Cette requête inclut :

+ + + +

Les serveurs Web attendent une requête du client puis la traitent quand elle arrive. Il répond ensuite au navigateur avec un message HTTP Response. La réponse contient un statut HTTP Response indiquant si, oui ou non, la requête a abouti. (ex : "200 OK" pour un succés, "404 Not Found" si la ressource ne peut être trouvée, "403 Forbidden" si l'utilisateur n'est pas autorisé à voir la ressource etc. Le corps d'une réponse aboutie à une requête  GET contiendrait la ressource demandée.

+ +

Quand une page HTML est retournée, elle est affichée par le navigateur. Le navigateur, nativement, pourra découvrir des liens vers d'autres ressources (ex : une page HTML intégre habituellement des pages JavaScript et CSS ), et enverra des requêtes séparées pour télécharger ces fichiers.

+ +

Les sites web dynamiques ou statiques (voir sections suivantes) utilisent les mêmes protocoles/modèles de communication.

+ +

Exemple de requête/réponse GET 

+ +

Vous faites une simple requête GET en cliquant sur un lien ou en faisant une recherche sur un site (sur une page de moteur de recherche par exemple). Une requête HTTP envoyée lorsque vous effectuez une recherche sur MDN pour les termes : "La relation Client-Serveur" ressemblera beaucoup à ce qui suit mais ne sera pas identique car des parties du message dépendent des paramètres de votre navigateur.

+ +
+

Note : Le format des messsages HTTP est défini par un standard web  (RFC7230). Vous n'avez pas besoin de connaître ce niveau de détails mais vous saurez au moins d'où vient tout ça !

+
+ +

La requête

+ +

Chaque ligne de la requête contient des informations sur celle-ci. La première partie est appelée l'en-tête ( header) et contient beaucoup de données utiles. De la même manière qu'un HTML head contient des informations utiles (pas le contenu réel qui lui, se trouve dans le corps (body) :

+ +
GET https://developer.mozilla.org/en-US/search?q=la+relation+Client+-+serveur&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1
+Host: developer.mozilla.org
+Connection: keep-alive
+Pragma: no-cache
+Cache-Control: no-cache
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Referer: https://developer.mozilla.org/en-US/
+Accept-Encoding: gzip, deflate, sdch, br
+Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7
+Accept-Language: en-US,en;q=0.8,es;q=0.6
+Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _gat=1; _ga=GA1.2.1688886003.1471911953; ffo=true
+
+ +

Les premières et secondes lignes contiennent la plupart des données déjà évoquées précédemment :

+ + + +

La dernière ligne contient des données sur les cookies côté client — vous observerez que dans ce cas, le cookie a une id pour gérer la session :    (Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; ...).

+ +

Les lignes restantes concernent le navigateur utilisé et les sortes de réponses qu'il peut accepter. Par exemple, vous pouvez voir ceci :

+ + + +

Les requêtes HTTP peuvent aussi avoir un corps mais dans ce cas précis, il est vide.

+ +

La réponse

+ +

La première partie de la réponse à cette requête est détaillée ci-dessous. L'en-tête contient les données suivantes :

+ + + +

À la fin du message nous avons le contenu du corps  — lequel contient le "vrai" HTML demandé par la requête.

+ +
HTTP/1.1 200 OK
+Server: Apache
+X-Backend-Server: developer1.webapp.scl3.mozilla.com
+Vary: Accept,Cookie, Accept-Encoding
+Content-Type: text/html; charset=utf-8
+Date: Wed, 07 Sep 2016 00:11:31 GMT
+Keep-Alive: timeout=5, max=999
+Connection: Keep-Alive
+X-Frame-Options: DENY
+Allow: GET
+X-Cache-Info: caching
+Content-Length: 41823
+
+
+
+<!DOCTYPE html>
+<html lang="en-US" dir="ltr" class="redesign no-js"  data-ffo-opensanslight=false data-ffo-opensans=false >
+<head prefix="og: http://ogp.me/ns#">
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
+  <script>(function(d) { d.className = d.className.replace(/\bno-js/, ''); })(document.documentElement);</script>
+  ...
+
+ +

Le reste de l'en-tête de la réponse contient des informations sur la réponse elle-même (quand elle a été générée), sur le serveur et comment le navigateur doit gérer la page ( X-Frame-Options: DENY cette ligne dit au navigateur de ne pas autoriser cette page a être intégrée dans une  {{htmlelement("iframe")}} dans un autre site).

+ +

Exemple de requête/réponse POST

+ +

Un POST HTTP est effectué lorsque vous soumettez un formulaire contenant des données à sauvegarder sur le serveur.

+ +

La requête

+ +

Le texte ci-dessous montre une requête HTTP faite quand un utlisateur soumet un nouveaux profil sur ce site. Le format de la requête est presque le même que celui de la requête GET vue précédemment, bien que la première ligne identifie cette requête comme un POST

+ +
POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1
+Host: developer.mozilla.org
+Connection: keep-alive
+Content-Length: 432
+Pragma: no-cache
+Cache-Control: no-cache
+Origin: https://developer.mozilla.org
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
+Content-Type: application/x-www-form-urlencoded
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
+Referer: https://developer.mozilla.org/en-US/profiles/hamishwillee/edit
+Accept-Encoding: gzip, deflate, br
+Accept-Language: en-US,en;q=0.8,es;q=0.6
+Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; _gat=1; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _ga=GA1.2.1688886003.1471911953; ffo=true
+
+csrfmiddlewaretoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT&user-username=hamishwillee&user-fullname=Hamish+Willee&user-title=&user-organization=&user-location=Australia&user-locale=en-US&user-timezone=Australia%2FMelbourne&user-irc_nickname=&user-interests=&user-expertise=&user-twitter_url=&user-stackoverflow_url=&user-linkedin_url=&user-mozillians_url=&user-facebook_url=
+ +

La principale différence est que l'URL ne comporte pas de paramètres.  Comme vous voyez, l'information du formulaire est encodée dans le corps de la requête (par exemple : le nom complet du nouvel utilisateur est paramétré avec  &user-fullname=Hamish+Willee).

+ +

La réponse

+ +

La réponse à la requête est expliquée dessous. Le statut "302 Found" dit au navigateur que le post a abouti et qu'il peut délivrer une deuxième requête HTTP pour charger la page spécifiée dans le champ  Location. L'information est donc en cela similaire à une réponse de requête GET.

+ +
HTTP/1.1 302 FOUND
+Server: Apache
+X-Backend-Server: developer3.webapp.scl3.mozilla.com
+Vary: Cookie
+Vary: Accept-Encoding
+Content-Type: text/html; charset=utf-8
+Date: Wed, 07 Sep 2016 00:38:13 GMT
+Location: https://developer.mozilla.org/en-US/profiles/hamishwillee
+Keep-Alive: timeout=5, max=1000
+Connection: Keep-Alive
+X-Frame-Options: DENY
+X-Cache-Info: not cacheable; request wasn't a GET or HEAD
+Content-Length: 0
+
+ +
+

Note : Les requêtes et réponses montrées dans ces exemples ont été capturées avec l'application Fiddler , mais vous pouvez avoir des informations similaires en utilisant des "renifleurs" web  (e.g. Websniffer, Wireshark) ou des extensions de navigateur comme  HttpFox. Vous pouvez essayer seul. Utilisez tous les outils recommandés, naviguez sur des sites et  éditez des profils de données pour explorer les différentes requêtes et réponses. La plupart des navigateurs modernes ont aussi des outils qui gérent les requêtes réseau, par exemple le Network Monitor dans Firefox).

+
+ +

Les sites statiques

+ +

Un site statique renvoie le même contenu codé en dur depuis le serveur quelle que soit la ressource demandée. Si vous avez une page concernant un produit à l'adresse  /static/myproduct1.html, cette même page sera retournée à chaque utilisateur. Si vous ajoutez un nouveau produit, vous devez ajouter une nouvelle page (par ex : myproduct2.html) et ainsi de suite. Cela peut être vraiment inefficace — Comment faire quand vous avez des milliers de pages "produit" à faire ? Vous allez répéter beaucoup de code identique dans chaque page (le modèle de base de la page, sa structure, etc.) et si vous voulez changer quoique ce soit dans la structure de la page — comme une section "produits dérivés" par exemple — alors, il faudra changer chaque page individuellement..

+ +
+

Note : Les sites statiques sont trés efficace quand vous avez un petit nombre de pages et que vous voulez envoyer le même contenu à chaque utilisateur. De toutes façons, ils peuvent avoir un coût certain de maintenance au fur et à mesure de l'augmentation du nombre de pages.

+
+ +

Voyons comment tout cela marche en révisant un diagramme d'architecture de site statique vu dans l'article précédent.

+ +

A simplified diagram of a static web server.

+ +

Quand un utilisateur veut naviguer jusqu'à une page, le navigateur envoie une requête HTTP GET spécifiant l'URL de sa page HTML. Le serveur retourne le document demandé depuis son système de fichiers et retourne une réponse  HTTP contenant le document et un  HTTP Response status code ( statut codé de la réponse HTTP) qui est  "200 OK" (indiquant le succés de l'opération). Le serveur peut retourner un statut différent, par exemple "404 Not Found"  si le fichier est absent sur le serveur , ou bien "301 Moved Permanently" si le fichier existe mais a été déplacé vers une nouvelle localisation.

+ +

Le serveur d'un site statique n'aura à faire face qu'à des requêtes  GET vu qu'il ne stocke aucune donnée modifiable. Il ne change pas non plus ses réponses basées sur les données des requêtes HTTP (c'est à dire les paramètres URL ou les cookies). 

+ +

Comprendre comment fonctionnent les sites statiques est sans aucun doute trés utile à l'apprentissage de la programmation côté serveur car les sites dynamiques gèrent les requêtes pour les fichiers statiques (CSS, JavaScript, images statiques , etc.) exactement de la même manière.

+ +

Les sites dynamiques

+ +

Un site dynamique peut générer et retourner du contenu basé sur une requête URL spécifique et les données (plutôt que de toujours renvoyer le même fichier codé en dur à une URL particulière).  Toujours avec l'exemple d'un site "produits", le serveur stockera les données du produit dans une base de données plutôt que dans un fichier HTML individuel. Quand il reçoit une requête HTTP GET pour un produit, le serveur détermine l'ID du produit, va chercher les données dans la base de données puis construit la page HTML pour la réponse en intégrant les données dans un gabarit (template) HTML. C'est un avantage indéniable sur un site statique :

+ +

Utiliser une base de données permet à l'information "produit" d'être stockée efficacement, en étant modifiable, extensible et bien indexée.

+ +

Employer des gabarits HTML facilite la façon de changer la structure HTML parce que c'est fait en un seul endroit, dans un seul gabarit (template) et non pas sur potentiellement des milliers de pages statiques.

+ +

Anatomie d'un requête dynamique

+ +

Cette section présente une vue d'ensemble du cycle dynamique HTTP de requête/réponse, construit avec ce que nous avons vu précédemment avec de plus amples détails. Toujours dans l'optique de "faire les choses en réel" nous utiliserons le contexte du site d'une équipe de sport où l'entraîneur peut sélectionner le nom de l'équipe et le nombre de joueurs dans un formulaire HTML et avoir en retour une suggestion "Meilleure composition" pour le prochain match.

+ +

Le diagramme ci-dessous montre les principaux éléments du site Web "entraîneur d'équipe", ainsi que des étiquettes numérotées pour la séquence des opérations lorsque l'entraîneur accède à la liste "meilleure équipe". Les parties du site qui le rendent dynamique sont l’application Web (c’est ainsi que nous nous référerons au code côté serveur qui traite les requêtes HTTP et renvoie les réponses HTTP), la base de données, qui contient des informations sur les joueurs, les équipes, les entraîneurs et leurs partenaires. relations, et les modèles HTML.

+ +

This is a diagram of a simple web server with step numbers for each of step of the client-server interaction.

+ +

Une fois que l’entraîneur a soumis le formulaire avec le nom de l’équipe et le nombre de joueurs, la séquence des opérations est la suivante:

+ +
    +
  1. Le navigateur Web crée une requête HTTP GET au serveur en utilisant l’URL de base de la ressource (/ best) et en codant l’équipe et le numéro du joueur sous forme de paramètres d’URL (par exemple / best? team=my_team_name&show = 11) ou dans le cadre de l’URL modèle (par exemple / best / my_team_name / 11 /). Une requête GET est utilisée car la requête extrait uniquement des données (sans les modifier).
  2. +
  3. Le serveur Web détecte que la demande est "dynamique" et la transmet à l'application Web pour traitement (le serveur Web détermine comment gérer différentes URL en fonction des règles de correspondance de modèle définies dans sa configuration).
  4. +
  5. L'application Web identifie l'objectif de la demande d'obtenir la "meilleure liste d'équipes" en fonction de l'URL (/ best /) et recherche le nom d'équipe requis et le nombre de joueurs à partir de l'URL. L'application Web obtient alors les informations requises de la base de données (en utilisant des paramètres "internes" supplémentaires pour définir quels joueurs sont les "meilleurs", et éventuellement en obtenant également l'identité de l'entraîneur connecté à partir d'un cookie côté client).
  6. +
  7. L'application Web crée dynamiquement une page HTML en plaçant les données (de la base de données) dans des espaces réservés dans un modèle HTML.
  8. +
  9. L'application Web renvoie le code HTML généré au navigateur Web (via le serveur Web), ainsi qu'un code d'état HTTP de 200 ("success"). Si quoi que ce soit empêche le code HTML d'être renvoyé, l'application Web renvoie un autre code, par exemple "404" pour indiquer que l'équipe n'existe pas.
  10. +
  11. Le navigateur Web commence alors à traiter le code HTML renvoyé, en envoyant des demandes distinctes pour obtenir tous les fichiers CSS ou JavaScript qu’il référence (voir étape 7).
  12. +
  13. Le serveur Web charge les fichiers statiques à partir du système de fichiers et les renvoie directement au navigateur (là encore, le traitement correct des fichiers est basé sur les règles de configuration et la correspondance des types d'URL).
  14. +
+ +

Une opération de mise à jour d'un enregistrement dans la base de données serait gérée de la même manière, sauf que, comme toute mise à jour de base de données, la demande HTTP du navigateur devrait être codée en tant que demande POST.

+ +

que faire d'autre?

+ +

Le travail d'une application Web consiste à recevoir des requêtes HTTP et à renvoyer des réponses HTTP. Bien que l'interaction avec une base de données pour obtenir ou mettre à jour des informations soit une tâche très courante, le code peut faire d'autres choses en même temps, ou ne pas interagir du tout avec une base de données.Un bon exemple de tâche supplémentaire qu'une application Web pourrait exécuter serait l'envoi d'un courrier électronique aux utilisateurs pour confirmer leur inscription sur le site. Le site peut également effectuer une journalisation ou d’autres opérations.

+ +

Renvoyer autre chose que  du HTML

+ +

Le code de site Web côté serveur ne doit pas nécessairement renvoyer des extraits / fichiers HTML dans la réponse. Au lieu de cela, il peut créer et renvoyer de manière dynamique d'autres types de fichiers (texte, PDF, CSV, etc.) ou même des données (JSON, XML, etc.).L'idée de renvoyer des données à un navigateur Web afin qu'il puisse mettre à jour de manière dynamique son propre contenu ({{glossary ("AJAX")}}) existe depuis un certain temps. Plus récemment, les "applications à page unique" sont devenues populaires, le site Web entier étant écrit avec un seul fichier HTML mis à jour de manière dynamique en cas de besoin. Les sites Web créés à l'aide de ce style d'application génèrent des coûts de calcul considérables entre le serveur et le navigateur Web, ce qui peut donner l'impression que les sites Web se comportent beaucoup plus comme des applications natives (très réactives, etc.).

+ +

Les frameworks Web simplifient la programmation Web côté serveur

+ +

Les infrastructures Web côté serveur facilitent beaucoup la rédaction de code permettant de gérer les opérations décrites ci-dessus.L’une des opérations les plus importantes qu’ils effectuent consiste à fournir des mécanismes simples pour mapper les URL de différentes ressources / pages à des fonctions de gestionnaire spécifiques. Cela facilite la séparation du code associé à chaque type de ressource. Cela présente également des avantages en termes de maintenance, car vous pouvez modifier l'URL utilisée pour fournir une fonctionnalité particulière à un endroit, sans avoir à changer la fonction de gestionnaire.Par exemple, considérons le code Django (Python) suivant qui mappe deux modèles d'URL à deux fonctions d'affichage. Le premier modèle garantit qu'une requête HTTP avec une URL de ressource / best sera transmise à une fonction nommée index () dans le module views. Une demande qui a pour motif "/ best / junior" sera plutôt transmise à la fonction d'affichage junior ().

+ +
# file: best/urls.py
+#
+
+from django.conf.urls import url
+
+from . import views
+
+urlpatterns = [
+    # example: /best/
+    url(r'^$', views.index),
+    # example: /best/junior/
+    url(r'^junior/$', views.junior),
+]
+ +
+

Note : Les premiers paramètres des fonctions url () peuvent paraître un peu bizarres (par exemple, r '^ junior / $') car ils utilisent une technique de correspondance de modèle appelée "expressions régulières" (RegEx ou RE). Vous n'avez pas besoin de savoir comment fonctionnent les expressions régulières à ce stade, car elles nous permettent également de faire correspondre les modèles de l'URL (plutôt que les valeurs codées en dur ci-dessus) et de les utiliser comme paramètres dans nos fonctions d'affichage. À titre d'exemple, un RegEx très simple pourrait dire "faire correspondre une seule lettre majuscule, suivie de 4 à 7 lettres minuscules".

+
+ +
L'infrastructure Web permet également à une fonction d'affichage d'extraire facilement des informations de la base de données. La structure de nos données est définie dans des modèles, qui sont des classes Python qui définissent les champs à stocker dans la base de données sous-jacente. Si nous avons un modèle nommé Team avec un champ "team_type", nous pouvons utiliser une syntaxe de requête simple pour récupérer toutes les équipes ayant un type particulier.
+ +
L’exemple ci-dessous donne la liste de toutes les équipes ayant le type d’équipe exact (sensible à la casse) de "junior" - notez le format: nom du champ (team_type) suivi du  double underscore, puis du type de match à utiliser (ici nous utilisons: exact). ). Il existe de nombreux autres types de match et nous pouvons les enchaîner. Nous pouvons également contrôler l'ordre et le nombre de résultats retournés.
+ +
+ +
#best/views.py
+
+from django.shortcuts import render
+
+from .models import Team
+
+
+def junior(request):
+    list_teams = Team.objects.filter(team_type__exact="junior")
+    context = {'list': list_teams}
+    return render(request, 'best/index.html', context)
+
+ +

Une fois que la fonction junior () a obtenu la liste des équipes juniors, elle appelle la fonction render () en transmettant la requête HttpRequest d'origine, un modèle HTML et un objet "context" définissant les informations à inclure dans le modèle. La fonction render () est une fonction pratique qui génère du HTML à l'aide d'un context et d'un template HTML, puis le renvoie dans un objet HttpResponse.De toute évidence, les frameworks Web peuvent vous aider dans de nombreuses autres tâches. Nous discutons beaucoup plus d'avantages et de choix de frameworks Web populaires dans le prochain article.

+ +

Summary

+ +

À ce stade, vous devez avoir une bonne vue d'ensemble des opérations que le code côté serveur doit effectuer et connaître certaines des manières dont une infrastructure Web côté serveur peut faciliter cela.

+ +

Dans un module suivant, nous vous aiderons à choisir le meilleur framework Web pour votre premier site.

+ +

{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}

+ +

In this module

+ + diff --git a/files/fr/learn/server-side/first_steps/index.html b/files/fr/learn/server-side/first_steps/index.html deleted file mode 100644 index cb79ff0b9a..0000000000 --- a/files/fr/learn/server-side/first_steps/index.html +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Premiers pas dans la programmation d'un site côté serveur -slug: Learn/Server-side/First_steps -tags: - - Débutant - - Encodage - - Guide - - Intro - - Programmation côté serveur -translation_of: Learn/Server-side/First_steps -original_slug: Learn/Server-side/Premiers_pas ---- -
{{LearnSidebar}}
- -

Dans ce module nous répondrons à quelques questions fondamentales sur la programmation côté serveur — "Qu'est-ce que c'est ?", "En quoi diffère-t-elle de la programmation côté client ?" et "Pourquoi est-ce si utile ?". Nous fournirons ensuite un aperçu de certaines infrastructures d'applications web (aussi appelé frameworks) côté serveurs parmi les plus populaires, ainsi que des conseils pour sélectionner la plus approprié pour créer votre premier site. Enfin un article vous présentera les questions de sécurité pour un serveur web.   

- -

Prérequis

- -

Pour suivre ce module, aucune connaissance en programmation web côté serveur ou tout autre type de programmation n'est nécessaire.

- -

Vous aurez besoin de comprendre "comment fonctionne le web". Nous vous recommandons de lire d'abord les sujets suivants :

- - - -

Avec cette compréhension de base, vous serez prêts à parcourir les modules de cette section.

- -

Guides

- -
-
Introduction au côté serveur
-
Bienvenue au cours de programmation débutant de MDN ! Dans ce premier article, nous examinerons la programmation côté serveur, répondant à des questions comme «En quoi consiste-t-elle ?», «En quoi diffère-t-elle de la programmation côté client ?» et «Pourquoi est-ce si utile ?». Après avoir lu cet article, vous comprendrez comment la programmation côté serveur donne aux sites web toute leur puissance.
-
Présentation client-serveur
-
Maintenant que vous connaissez le but et les avantages potentiels de la programmation côté serveur, nous allons examiner en détail ce qui se passe lorsqu'un serveur reçoit une "requête dynamique" d'un navigateur. Comme la plupart des codes côté serveur traitent les demandes et les réponses de la même manière, cela vous aidera à comprendre ce que vous devez faire lorsque vous écrivez votre propre code.
-
Frameworks web côté serveur
-
Le dernier article vous a montré ce qu'une application web côté serveur doit faire pour répondre aux demandes d'un navigateur web. Nous allons maintenant vous montrer comment les frameworks web peuvent simplifier ces tâches ; nous vous aiderons à choisir le bon framework pour coder votre première application web côté serveur.
-
Sécurité de votre site web
-
La sécurité de votre site web requiert une vigilance dans tous les aspects de sa conception et de son utilisation. Cet article d'introduction ne fera pas de vous un gourou de la sécurité des sites web, mais il vous aidera à comprendre les premières mesures importantes à prendre pour mettre votre application web à l'abri des menaces les plus courantes.
-
- -

Évaluations

- -

Ce module "aperçu" n'a aucune évaluation car nous ne vous avons pas encore montré de code. Nous espérons à ce stade que vous avez une bonne compréhension des types de fonctionnalités que vous pouvez fournir en utilisant la programmation côté serveur et que vous avez pris une décision quant à l'infrastructure web côté serveur que vous utiliserez pour créer votre premier site web.

diff --git a/files/fr/learn/server-side/first_steps/index.md b/files/fr/learn/server-side/first_steps/index.md new file mode 100644 index 0000000000..cb79ff0b9a --- /dev/null +++ b/files/fr/learn/server-side/first_steps/index.md @@ -0,0 +1,46 @@ +--- +title: Premiers pas dans la programmation d'un site côté serveur +slug: Learn/Server-side/First_steps +tags: + - Débutant + - Encodage + - Guide + - Intro + - Programmation côté serveur +translation_of: Learn/Server-side/First_steps +original_slug: Learn/Server-side/Premiers_pas +--- +
{{LearnSidebar}}
+ +

Dans ce module nous répondrons à quelques questions fondamentales sur la programmation côté serveur — "Qu'est-ce que c'est ?", "En quoi diffère-t-elle de la programmation côté client ?" et "Pourquoi est-ce si utile ?". Nous fournirons ensuite un aperçu de certaines infrastructures d'applications web (aussi appelé frameworks) côté serveurs parmi les plus populaires, ainsi que des conseils pour sélectionner la plus approprié pour créer votre premier site. Enfin un article vous présentera les questions de sécurité pour un serveur web.   

+ +

Prérequis

+ +

Pour suivre ce module, aucune connaissance en programmation web côté serveur ou tout autre type de programmation n'est nécessaire.

+ +

Vous aurez besoin de comprendre "comment fonctionne le web". Nous vous recommandons de lire d'abord les sujets suivants :

+ + + +

Avec cette compréhension de base, vous serez prêts à parcourir les modules de cette section.

+ +

Guides

+ +
+
Introduction au côté serveur
+
Bienvenue au cours de programmation débutant de MDN ! Dans ce premier article, nous examinerons la programmation côté serveur, répondant à des questions comme «En quoi consiste-t-elle ?», «En quoi diffère-t-elle de la programmation côté client ?» et «Pourquoi est-ce si utile ?». Après avoir lu cet article, vous comprendrez comment la programmation côté serveur donne aux sites web toute leur puissance.
+
Présentation client-serveur
+
Maintenant que vous connaissez le but et les avantages potentiels de la programmation côté serveur, nous allons examiner en détail ce qui se passe lorsqu'un serveur reçoit une "requête dynamique" d'un navigateur. Comme la plupart des codes côté serveur traitent les demandes et les réponses de la même manière, cela vous aidera à comprendre ce que vous devez faire lorsque vous écrivez votre propre code.
+
Frameworks web côté serveur
+
Le dernier article vous a montré ce qu'une application web côté serveur doit faire pour répondre aux demandes d'un navigateur web. Nous allons maintenant vous montrer comment les frameworks web peuvent simplifier ces tâches ; nous vous aiderons à choisir le bon framework pour coder votre première application web côté serveur.
+
Sécurité de votre site web
+
La sécurité de votre site web requiert une vigilance dans tous les aspects de sa conception et de son utilisation. Cet article d'introduction ne fera pas de vous un gourou de la sécurité des sites web, mais il vous aidera à comprendre les premières mesures importantes à prendre pour mettre votre application web à l'abri des menaces les plus courantes.
+
+ +

Évaluations

+ +

Ce module "aperçu" n'a aucune évaluation car nous ne vous avons pas encore montré de code. Nous espérons à ce stade que vous avez une bonne compréhension des types de fonctionnalités que vous pouvez fournir en utilisant la programmation côté serveur et que vous avez pris une décision quant à l'infrastructure web côté serveur que vous utiliserez pour créer votre premier site web.

diff --git a/files/fr/learn/server-side/first_steps/introduction/index.html b/files/fr/learn/server-side/first_steps/introduction/index.html deleted file mode 100644 index 8a3337f276..0000000000 --- a/files/fr/learn/server-side/first_steps/introduction/index.html +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: Présentation du côté serveur -slug: Learn/Server-side/First_steps/Introduction -tags: - - Apprendre - - Débutant - - Guide - - Intro - - Programmation côté serveur - - Serveur -translation_of: Learn/Server-side/First_steps/Introduction -original_slug: Learn/Server-side/Premiers_pas/Introduction ---- -
{{LearnSidebar}}
- -
{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}
- -

Bienvenue au cours pour débutant de MDN sur la programmation côté serveur. Dans ce premier article, nous allons regarder la programmation côté serveur sans rentrer dans les détails techniques, en répondant aux questions telles que "qu'est-ce que c'est?", "quelle est la différence avec la programmation côté client?", et "pourquoi est-ce utile?". Après avoir lu cet article vous comprendrez commment la programmation côté serveur donne toute leur puissance aux sites web.

- - - - - - - - - - - - -
Prérequis:Connaissances de base en informatique. Une compréhension de base de ce qu'est un serveur web.
Objectif:Se familiariser avec la programmation côté serveur, ce qu'elle peut faire, et en quoi elle diffère de la programmation côté client.
- -

La plupart des sites web à grande échelle utilisent du code côté serveur pour afficher dynamiquement différentes données lorsque nécessaire ; ces données sont généralement extraites d'une base de données stockée sur un serveur et envoyées au client pour être affichées avec du code (HTML et/ou JavaScript). 

- -

L'avantage le plus significatif du code côté serveur est sans doute qu'il permet d'adapter le contenu du site web à chaque utilisateur. Les sites dynamiques peuvent mettre en évidence du contenu pertinent en fonction des préférences et des habitudes de l'utilisateur. Il peut également faciliter l'utililsation des sites web en stockant des données, des informations personnelles — par exemple donner la possibilité d'enregistrer une carte de crédit pour les achats suivants.

- -

Cela peut même permettre une interaction avec les utilisateurs du site, en envoyant des notifications et mises à jour par email ou via d'autres canaux. Toutes ces capacités rendent possible un engagement plus important avec l'utilisateur.

- -

Dans le monde moderne du développement web, apprendre le développement côté serveur est fortement recommandé.

- -

Qu'est-ce que la programmation côté serveur?

- -

Les navigateurs web communiquent avec les serveurs web en utilisant le protocole {{glossary("HTTP")}} (HyperText Transfer Protocol). Quand vous cliquez sur un lien dans une page web, envoyez un formulaire, ou encore lancez une recherche, une requête HTTP est envoyée du navigateur au serveur cible.

- -

Une requête inclut une URL pour identifier la ressource demandée, une méthode pour définir l'action désirée (comme GET pour obtenir, DELETE pour supprimer ou POST pour publier) et peut également inclure des informations supplémentaires encodées dans les paramètres de l'URL (des paires clés/valeurs envoyées via une chaîne de recherche — query string en anglais), les données POST (données envoyées par la méthode HTTP POST), ou les {{glossary("Cookie", "cookies associés")}}.

- -

Les serveurs web attendent des requêtes du client, les traitent quand elles arrivent, et répondent au navigateur web avec une réponse HTTP. La réponse contient un statut qui indique si la requête a pu être traitée avec succès ou non (exemple "HTTP/1.1 200 OK" pour indiquer le succès). 

- -

Le corps de la réponse, si la requête réussit, contient alors la ressource demandée (comme une page HTML, une image, etc...), que le navigateur web peut alors afficher.

- -

Sites statiques

- -

Le diagramme ci-dessous montre l'architecture d'un serveur web basique pour un site statique (un site statique est un site qui renvoie du contenu codé en dur, c'est à dire le contenu d'un fichier, quand une ressource donnée est demandée). Quand un utilisateur veut naviguer sur une page, le navigateur envoie une requête HTTP "GET" spécifiant son URL.

- -

Le serveur récupère le document demandé du système de fichiers et retourne une réponse HTTP contenant le document et le statut de la réponse (habituellement, 200 OK). Si le fichier ne peut pas être recupéré pour une raison x ou y, le statut d'erreur est retourné (voir réponses d'erreur client et réponse d'erreur serveur).

- -

A simplified diagram of a static web server.

- -

Sites dynamiques

- -

Un site web dynamique, quant à lui, est un site dont une partie des réponses sont générées dynamiquement, à la demande. Sur les sites dynamiques, les pages HTML sont normalement crées en insérant des données d'une base de données dans des espaces réservés à l'intérieur de templates HTML (c'est une manière beaucoup plus efficace que des fichiers statiques pour stocker de grandes quantités de contenu). 

- -

Un site dynamique peut retourner des données différentes pour une URL, en se basant sur les informations fournies par l'utilisateur ou les préférences stockées et peut effectuer des opérations avant de retourner la réponse.

- -

La plupart du code pour maintenir un site web dynamique doit s'exécuter sur le serveur. La création de ce code est ce qu'on appelle la "programmation côté serveur" (ou parfois "codage back-end").

- -

Le diagramme ci-dessous montre une architecture simple pour un site web dynamique. Comme dans le diagramme précédent, les navigateurs envoient des requêtes HTTP au serveur, qui les traite et retourne les réponses HTTP appropriées.

- -

Les requêtes pour les ressources statiques sont toujours gérées de la même manière que pour les sites statiques (les ressources statiques sont tous les fichiers qui ne changent pas —typiquement : CSS, JavaScript, Images, fichiers PDF etc).

- -

A simplified diagram of a web server that uses server-side programming to get information from a database and construct HTML from templates. This is the same diagram as is in the Client-Server overview.

- -

Les requêtes pour les ressources dynamiques, elles, sont transmises (2) au code côté serveur (indiqué dans le diagramme comme Web Application). Le serveur interprète la requête, lit les informations correspondantes dans la base de données (3), combine les données récupérées avec les templates HTML (4), et renvoie la réponse avec le HTML généré (5,6). 

- -
-

La programmation côté serveur c'est pareil que côté client?

-
- -

Voyons maintenant le code impliqué dans la programmation côté serveur et côté client. Dans chaque cas, le code est significativement différent :

- - - -

Le code exécuté par le navigateur est connu sous le nom de code côté client (ou front-end) et se préoccupe principalement de l'apparence et du comportement des pages web affichées. Cela inclut sélectionner et styliser les composants de l'interface utilisateur, créer la mise en page, la navigation, valider les formulaires, etc. D'un autre côté, la programmation côté serveur implique de définir le contenu retourné au navigateur en réponse aux requêtes. Le code côté serveur gère des tâches telles que la validation des données envoyées, l'utilisation des bases de données pour stocker et récupérer des données et l'envoi de données générées au client tel qu'attendu.

- -

Le code client est écrit en HTMLCSS, et JavaScript — il est exécuté dans un navigateur web et a peu ou pas accès au système d'exploitation de l'utilisateur (inclut un accès limité au système de fichiers).

- -

Les développeurs web ne peuvent pas contrôler quel navigateur est utilisé par l'utilisateur pour voir le site web  — or les navigateurs fournissent des niveaux de compatibilité inconsistants quant aux fonctionnalités du code côté client, et une partie du challenge de la programmation côté client consiste à gérer les différences de support des navigateurs.

- -

Le code côté serveur peut être écrit dans nombre de langages de programmation — les langages les plus populaires pour la programmation web côté serveur sont en autres PHP, Python, Ruby, C#, et NodeJS(JavaScript). Le code côté serveur a plein accès au système d'exploitation du serveur et le développeur est libre de choisir le langage (et la version) qu'il veut utiliser.

- -

Typiquement, les développeurs écrivent leur code en utilisant des frameworks web. Les frameworks web sont des ensembles de fonctions, objets, règles et autres constructions de code conçus pour résoudre des problèmes courants, accélérer le développement et simplifier les différents types de tâches rencontrées dans un domaine particulier.

- -

Encore une fois, bien que le code côté client et côté serveur utilisent des frameworks, les domaines d'application sont très différents et par conséquent les frameworks aussi. Les frameworks web côté client simplifient les tâches de mise en page et de présentation tandis que les frameworks web côté serveur fournissent des fonctionnalités "courantes" que vous auriez probablement à implémenter vous-même autrement (comme le support des sessions, des utilisateurs et de l'authentification, l'accès à la base de données, les bibliothèques de templates, etc.).

- -
-

Note : Les frameworks côté client sont souvent utilisés pour accélérer le développement du code côté client, mais vous pouvez également choisir d'écrire tout le code à la main ; en vérité, écrire votre code à la main peut être plus rapide et plus efficace si vous n'avez besoin que d'une petite interface web très simple.

- -

En revanche, vous ne penseriez presque jamais à écrire les composants côté serveur d'une application web sans framework — implémenter des fonctionnalités vitales comme un serveur HTTP est très difficile à faire à partir de rien, comme disons en Python, alors que les frameworks web Python comme Django le fournissent tout prêt à l'emploi, accompagné d'autres outils très utiles.

-
- -
-

Que peut-on faire côté serveur?

- -

La programmation côté serveur est très utile parce qu'elle nous permet de délivrer efficacement de l'information taillée sur mesure pour l'utilisateur et ainsi créer une bien meilleure expérience utilisateur.

-
- -

Des compagnies comme Amazon utilisent la programmation côté serveur pour construire la recherche de produits, faire des suggestions de produit ciblées sur les préférences du client et ses habitudes d'achat, simplifier les achats, etc.

- -

Les banques l'utilisent pour stocker les informations du compte ainsi que faire des transactions et n'autoriser à les consulter que les utilisateurs reconnus. D'autres services comme Facebook, Twitter, Instagram, et Wikipedia utilisent la programmation côté serveur pour mettre en avant, partager et contrôler l'accès au contenu.

- -

Les utilisations les plus courantes et les plus bénéfiques de la programmation côté serveur sont listées ci-dessous. Vous verrez qu'il y a quelques recoupements :

- -

Stockage et distribution de l'information plus efficaces

- -

Imaginez combien de produits sont disponibles sur Amazon et combien de posts ont été écrits sur Facebook. Créer une page statique distincte pour chaque produit ou article serait totalement impossible.

- -

La programmation côté serveur nous permet plutôt de stocker l'information dans une base de données et de construire et retourner dynamiquement le HTML ainsi que d'autres types de fichiers (comme les PDF, images, etc.). Il est également possible de simplement retourner des données ({{glossary("JSON")}}, {{glossary("XML")}}, etc.) pour les afficher avec des frameworks côté client (cela réduit la charge de travail du serveur et la quantité de données qui doit être retournée).

- -

Le serveur ne se limite pas à l'envoi d'informations à partir de bases de données, il peut retourner le résultat d'autres outils logiciels, ou les données de services de communication. Le contenu peut même être ciblé pour le type d'appareil client qui le reçoit.

- -

Comme les informations se trouvent dans une base de données, elles peuvent également être partagées et mises à jour plus facilement avec d'autres systèmes (par exemple, quand des produits sont vendus en ligne ou dans un magasin, le magasin peut mettre à jour son inventaire).

- -
-

Note : Votre imagination n'a pas à travailler dur pour voir les bénéfices du code côté serveur pour le stockage et distribution de l'information:

- -
    -
  1. Allez sur Amazon ou tout autre site e-commerce.
  2. -
  3. Cherchez un certain nombre de mot-clés et remarquez que la structure de la page de change pas, même si les résultats oui. 
  4. -
  5. Ouvrez deux ou trois produits. Remarquez que la structure et la disposition de la page sont identiques, mais que le contenu pour les différents produits a été extrait de la base de données.
  6. -
- -

Pour un terme de recherche courant  ("poisson", disons) vous pouvez voir littéralement des millions de valeurs retournées. Utiliser une base de données permet à ces données d'être stockées et partagées efficacement, et permet de contrôler la présentation de l'information à partir d'un seul endroit.

-
- -

Expérience utilisateur personnalisée

- -

Les serveurs peuvent stocker et utiliser des informations sur les clients pour fournir une expérience utilisateur personnalisée. Par exemple, beaucoup de sites proposent d'enregistrer une carte de crédit pour que les détails n'aient pas à être saisis de nouveau. Des sites comme Google Maps peuvent utiliser les emplacement enregistrés ou l'emplacement en cours pour fournir des informations d'itinéraire et chercher ou utiliser l'historique des voyages précédents pour trouver des boutiques locales dans les résultats de recherche.

- -

Une analyse plus approfondie des habitudes des utilisateurs peut être utilisée pour anticiper leurs intérêts et personnaliser les réponses ou les notifications du serveur, par exemple pour fournir une liste des lieux précédemment visités ou les plus populaires que vous pourriez vouloir chercher sur la carte.

- -
-

Note : Google Maps sauvegarde vos recherches et votre historique de visites. Les emplacement fréquemment visités ou fréquemment recherchés sont plus mis en avant que les autres.

- -

Les résultats de recherche Google sont optimisés en fonction des recherches précédentes.

- -
    -
  1.  Allez sur Google.
  2. -
  3.  Recherchez "football".
  4. -
  5.  Maintenant tapez "favoris" dans la barre de recherche et regardez les prédictions de recherche de l'autocomplete.
  6. -
- -

Coïncidence ? Nada!

-
- -

Accès contrôlé au contenu

- -

La programmation côté serveur permet aux sites de restreindre l'accès aux utilisateurs autorisés et de ne servir que les informations qu'un utilisateur à la permission de voir.

- -

Quelques exemples du monde réel incluent :

- - - -
-

Note : Il existe de nombreux autres exemples où l'accès au contenu est contrôlé. Par exemple, que voyez-vous si vous allez sur le site en ligne de votre banque ? Connectez-vous à votre compte — quelles autres informations pouvez-vous voir et modifier ? Quelles informations pouvez-vous voir que seule la banque peut changer ?

-
- -

Stocker les informations de session/d'état

- -

La programmation côté serveur permet aux développeurs d'utiliser des sessions — en gros, un mécanisme qui permet au serveur de stocker des informations sur l'utilisation en cours d'un site et d'envoyer des réponses différentes selon cette information.

- -

Cela permet, par exemple, à un site de savoir qu'un utilisateur s'est déjà identifié et afficher des messages qui lui sont destinés, d'afficher son historique de commande, ou peut-être encore, dans le cas d'un jeu, lui permettre de reprendre là où il en est resté.

- -
-

Note : Visitez le site d'un journal qui a une offre d'abonnement et ouvrez des pages (par exemple The Age). Si vous continuez à visiter le site quelques heures/jours, éventuellement, vous commencerez à être redirigé vers des pages expliquant comment vous abonner, et vous ne pourrez plus accéder aux articles. Cette information est un exemple de session stockée dans des cookies.

-
- -

Notifications et communication

- -

Les serveurs peuvent envoyer des notifications générales ou personnalisées à l'utilisateur via le site web lui-même ou par email, SMS, messagerie instantannée, conversations vidéo ou autres services de communication.

- -

Quelques exemples incluent :

- - - -
-

Note : Le type de notification le plus courant est la "confirmation d'inscription". Choisissez presque n'importe quel site qui vous intéresse (Google, Amazon, Instagram, etc.) et créez un nouveau compte en utilisant votre adresse email. Vous recevrez rapidement un email qui confirme votre inscription, ou qui exige une confirmation pour activer votre compte.

-
- -

Analyse des données

- -

Un site web peut collecter beaucoup de données sur les utilisateurs : ce qu'ils cherchent, ce qu'ils achètent, ce qu'ils recommandent, combien de temps ils restent sur chaque page. La programmation côté serveur peut être utilisée pour affiner les réponses en fonction de l'analyse de ces données.

- -

Par exemple, Amazon et Google font tous deux de la publicité pour des produits en se basant sur les recherches précédentes, sur les achats que vous avez faits.

- -
-

Note : Si vous êtes un utilisateur de Facebook, allez sur votre flux principal et regardez les posts. Notez que certains posts ne sont pas classés par ordre numérique — en particulier, les posts qui ont le plus de "likes" sont souvent placés plus haut dans la liste que les posts plus récents.

- -

Observez également quels types de publicités vous voyez — vous pourrez voir des publicités pour des choses que vous avez regardé sur d'autres sites. L'algorithme de Facebook pour mettre en avant du contenu et faire de la publicité est un peu un mystérieux, mais il est clair qu'il prend en compte vos likes et ce que vous avez l'habitude de regarder !

-
- -

Sommaire

- -

Félicitations, vous avez atteint la fin du premier article sur la programmation côté serveur. 

- -

Vous avez maintenant appris que le code côté serveur est exécuté sur un serveur web et que son rôle principal est de contrôler quelle information est envoyée à l'utilisateur (tandis que le code côté client gère principalement la structure et la présentation des données pour l'utilisateur).

- -

Vous devez également comprendre que c'est utile pour créer des sites web qui délivrent de l'information efficacement, adaptée à chaque utilisateur et avoir une bonne idée de quelques choses que vous seriez capable de faire quand vous serez programmeur back-end.

- -

Finalement, vous devez comprendre que le code côté serveur peut être écrit dans de nombreux langages de programmation et que l'on peut utiliser des frameworks web pour rendre ce processus plus facile.

- -

Dans un futur article,  nous vous aiderons à choisir le framework le plus adapté pour la création d'un premier site. Ensuite, nous vous présenterons les principales interactions client-serveur plus en détails.

- -

{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}

- -

Dans ce module

- - diff --git a/files/fr/learn/server-side/first_steps/introduction/index.md b/files/fr/learn/server-side/first_steps/introduction/index.md new file mode 100644 index 0000000000..8a3337f276 --- /dev/null +++ b/files/fr/learn/server-side/first_steps/introduction/index.md @@ -0,0 +1,235 @@ +--- +title: Présentation du côté serveur +slug: Learn/Server-side/First_steps/Introduction +tags: + - Apprendre + - Débutant + - Guide + - Intro + - Programmation côté serveur + - Serveur +translation_of: Learn/Server-side/First_steps/Introduction +original_slug: Learn/Server-side/Premiers_pas/Introduction +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}
+ +

Bienvenue au cours pour débutant de MDN sur la programmation côté serveur. Dans ce premier article, nous allons regarder la programmation côté serveur sans rentrer dans les détails techniques, en répondant aux questions telles que "qu'est-ce que c'est?", "quelle est la différence avec la programmation côté client?", et "pourquoi est-ce utile?". Après avoir lu cet article vous comprendrez commment la programmation côté serveur donne toute leur puissance aux sites web.

+ + + + + + + + + + + + +
Prérequis:Connaissances de base en informatique. Une compréhension de base de ce qu'est un serveur web.
Objectif:Se familiariser avec la programmation côté serveur, ce qu'elle peut faire, et en quoi elle diffère de la programmation côté client.
+ +

La plupart des sites web à grande échelle utilisent du code côté serveur pour afficher dynamiquement différentes données lorsque nécessaire ; ces données sont généralement extraites d'une base de données stockée sur un serveur et envoyées au client pour être affichées avec du code (HTML et/ou JavaScript). 

+ +

L'avantage le plus significatif du code côté serveur est sans doute qu'il permet d'adapter le contenu du site web à chaque utilisateur. Les sites dynamiques peuvent mettre en évidence du contenu pertinent en fonction des préférences et des habitudes de l'utilisateur. Il peut également faciliter l'utililsation des sites web en stockant des données, des informations personnelles — par exemple donner la possibilité d'enregistrer une carte de crédit pour les achats suivants.

+ +

Cela peut même permettre une interaction avec les utilisateurs du site, en envoyant des notifications et mises à jour par email ou via d'autres canaux. Toutes ces capacités rendent possible un engagement plus important avec l'utilisateur.

+ +

Dans le monde moderne du développement web, apprendre le développement côté serveur est fortement recommandé.

+ +

Qu'est-ce que la programmation côté serveur?

+ +

Les navigateurs web communiquent avec les serveurs web en utilisant le protocole {{glossary("HTTP")}} (HyperText Transfer Protocol). Quand vous cliquez sur un lien dans une page web, envoyez un formulaire, ou encore lancez une recherche, une requête HTTP est envoyée du navigateur au serveur cible.

+ +

Une requête inclut une URL pour identifier la ressource demandée, une méthode pour définir l'action désirée (comme GET pour obtenir, DELETE pour supprimer ou POST pour publier) et peut également inclure des informations supplémentaires encodées dans les paramètres de l'URL (des paires clés/valeurs envoyées via une chaîne de recherche — query string en anglais), les données POST (données envoyées par la méthode HTTP POST), ou les {{glossary("Cookie", "cookies associés")}}.

+ +

Les serveurs web attendent des requêtes du client, les traitent quand elles arrivent, et répondent au navigateur web avec une réponse HTTP. La réponse contient un statut qui indique si la requête a pu être traitée avec succès ou non (exemple "HTTP/1.1 200 OK" pour indiquer le succès). 

+ +

Le corps de la réponse, si la requête réussit, contient alors la ressource demandée (comme une page HTML, une image, etc...), que le navigateur web peut alors afficher.

+ +

Sites statiques

+ +

Le diagramme ci-dessous montre l'architecture d'un serveur web basique pour un site statique (un site statique est un site qui renvoie du contenu codé en dur, c'est à dire le contenu d'un fichier, quand une ressource donnée est demandée). Quand un utilisateur veut naviguer sur une page, le navigateur envoie une requête HTTP "GET" spécifiant son URL.

+ +

Le serveur récupère le document demandé du système de fichiers et retourne une réponse HTTP contenant le document et le statut de la réponse (habituellement, 200 OK). Si le fichier ne peut pas être recupéré pour une raison x ou y, le statut d'erreur est retourné (voir réponses d'erreur client et réponse d'erreur serveur).

+ +

A simplified diagram of a static web server.

+ +

Sites dynamiques

+ +

Un site web dynamique, quant à lui, est un site dont une partie des réponses sont générées dynamiquement, à la demande. Sur les sites dynamiques, les pages HTML sont normalement crées en insérant des données d'une base de données dans des espaces réservés à l'intérieur de templates HTML (c'est une manière beaucoup plus efficace que des fichiers statiques pour stocker de grandes quantités de contenu). 

+ +

Un site dynamique peut retourner des données différentes pour une URL, en se basant sur les informations fournies par l'utilisateur ou les préférences stockées et peut effectuer des opérations avant de retourner la réponse.

+ +

La plupart du code pour maintenir un site web dynamique doit s'exécuter sur le serveur. La création de ce code est ce qu'on appelle la "programmation côté serveur" (ou parfois "codage back-end").

+ +

Le diagramme ci-dessous montre une architecture simple pour un site web dynamique. Comme dans le diagramme précédent, les navigateurs envoient des requêtes HTTP au serveur, qui les traite et retourne les réponses HTTP appropriées.

+ +

Les requêtes pour les ressources statiques sont toujours gérées de la même manière que pour les sites statiques (les ressources statiques sont tous les fichiers qui ne changent pas —typiquement : CSS, JavaScript, Images, fichiers PDF etc).

+ +

A simplified diagram of a web server that uses server-side programming to get information from a database and construct HTML from templates. This is the same diagram as is in the Client-Server overview.

+ +

Les requêtes pour les ressources dynamiques, elles, sont transmises (2) au code côté serveur (indiqué dans le diagramme comme Web Application). Le serveur interprète la requête, lit les informations correspondantes dans la base de données (3), combine les données récupérées avec les templates HTML (4), et renvoie la réponse avec le HTML généré (5,6). 

+ +
+

La programmation côté serveur c'est pareil que côté client?

+
+ +

Voyons maintenant le code impliqué dans la programmation côté serveur et côté client. Dans chaque cas, le code est significativement différent :

+ + + +

Le code exécuté par le navigateur est connu sous le nom de code côté client (ou front-end) et se préoccupe principalement de l'apparence et du comportement des pages web affichées. Cela inclut sélectionner et styliser les composants de l'interface utilisateur, créer la mise en page, la navigation, valider les formulaires, etc. D'un autre côté, la programmation côté serveur implique de définir le contenu retourné au navigateur en réponse aux requêtes. Le code côté serveur gère des tâches telles que la validation des données envoyées, l'utilisation des bases de données pour stocker et récupérer des données et l'envoi de données générées au client tel qu'attendu.

+ +

Le code client est écrit en HTMLCSS, et JavaScript — il est exécuté dans un navigateur web et a peu ou pas accès au système d'exploitation de l'utilisateur (inclut un accès limité au système de fichiers).

+ +

Les développeurs web ne peuvent pas contrôler quel navigateur est utilisé par l'utilisateur pour voir le site web  — or les navigateurs fournissent des niveaux de compatibilité inconsistants quant aux fonctionnalités du code côté client, et une partie du challenge de la programmation côté client consiste à gérer les différences de support des navigateurs.

+ +

Le code côté serveur peut être écrit dans nombre de langages de programmation — les langages les plus populaires pour la programmation web côté serveur sont en autres PHP, Python, Ruby, C#, et NodeJS(JavaScript). Le code côté serveur a plein accès au système d'exploitation du serveur et le développeur est libre de choisir le langage (et la version) qu'il veut utiliser.

+ +

Typiquement, les développeurs écrivent leur code en utilisant des frameworks web. Les frameworks web sont des ensembles de fonctions, objets, règles et autres constructions de code conçus pour résoudre des problèmes courants, accélérer le développement et simplifier les différents types de tâches rencontrées dans un domaine particulier.

+ +

Encore une fois, bien que le code côté client et côté serveur utilisent des frameworks, les domaines d'application sont très différents et par conséquent les frameworks aussi. Les frameworks web côté client simplifient les tâches de mise en page et de présentation tandis que les frameworks web côté serveur fournissent des fonctionnalités "courantes" que vous auriez probablement à implémenter vous-même autrement (comme le support des sessions, des utilisateurs et de l'authentification, l'accès à la base de données, les bibliothèques de templates, etc.).

+ +
+

Note : Les frameworks côté client sont souvent utilisés pour accélérer le développement du code côté client, mais vous pouvez également choisir d'écrire tout le code à la main ; en vérité, écrire votre code à la main peut être plus rapide et plus efficace si vous n'avez besoin que d'une petite interface web très simple.

+ +

En revanche, vous ne penseriez presque jamais à écrire les composants côté serveur d'une application web sans framework — implémenter des fonctionnalités vitales comme un serveur HTTP est très difficile à faire à partir de rien, comme disons en Python, alors que les frameworks web Python comme Django le fournissent tout prêt à l'emploi, accompagné d'autres outils très utiles.

+
+ +
+

Que peut-on faire côté serveur?

+ +

La programmation côté serveur est très utile parce qu'elle nous permet de délivrer efficacement de l'information taillée sur mesure pour l'utilisateur et ainsi créer une bien meilleure expérience utilisateur.

+
+ +

Des compagnies comme Amazon utilisent la programmation côté serveur pour construire la recherche de produits, faire des suggestions de produit ciblées sur les préférences du client et ses habitudes d'achat, simplifier les achats, etc.

+ +

Les banques l'utilisent pour stocker les informations du compte ainsi que faire des transactions et n'autoriser à les consulter que les utilisateurs reconnus. D'autres services comme Facebook, Twitter, Instagram, et Wikipedia utilisent la programmation côté serveur pour mettre en avant, partager et contrôler l'accès au contenu.

+ +

Les utilisations les plus courantes et les plus bénéfiques de la programmation côté serveur sont listées ci-dessous. Vous verrez qu'il y a quelques recoupements :

+ +

Stockage et distribution de l'information plus efficaces

+ +

Imaginez combien de produits sont disponibles sur Amazon et combien de posts ont été écrits sur Facebook. Créer une page statique distincte pour chaque produit ou article serait totalement impossible.

+ +

La programmation côté serveur nous permet plutôt de stocker l'information dans une base de données et de construire et retourner dynamiquement le HTML ainsi que d'autres types de fichiers (comme les PDF, images, etc.). Il est également possible de simplement retourner des données ({{glossary("JSON")}}, {{glossary("XML")}}, etc.) pour les afficher avec des frameworks côté client (cela réduit la charge de travail du serveur et la quantité de données qui doit être retournée).

+ +

Le serveur ne se limite pas à l'envoi d'informations à partir de bases de données, il peut retourner le résultat d'autres outils logiciels, ou les données de services de communication. Le contenu peut même être ciblé pour le type d'appareil client qui le reçoit.

+ +

Comme les informations se trouvent dans une base de données, elles peuvent également être partagées et mises à jour plus facilement avec d'autres systèmes (par exemple, quand des produits sont vendus en ligne ou dans un magasin, le magasin peut mettre à jour son inventaire).

+ +
+

Note : Votre imagination n'a pas à travailler dur pour voir les bénéfices du code côté serveur pour le stockage et distribution de l'information:

+ +
    +
  1. Allez sur Amazon ou tout autre site e-commerce.
  2. +
  3. Cherchez un certain nombre de mot-clés et remarquez que la structure de la page de change pas, même si les résultats oui. 
  4. +
  5. Ouvrez deux ou trois produits. Remarquez que la structure et la disposition de la page sont identiques, mais que le contenu pour les différents produits a été extrait de la base de données.
  6. +
+ +

Pour un terme de recherche courant  ("poisson", disons) vous pouvez voir littéralement des millions de valeurs retournées. Utiliser une base de données permet à ces données d'être stockées et partagées efficacement, et permet de contrôler la présentation de l'information à partir d'un seul endroit.

+
+ +

Expérience utilisateur personnalisée

+ +

Les serveurs peuvent stocker et utiliser des informations sur les clients pour fournir une expérience utilisateur personnalisée. Par exemple, beaucoup de sites proposent d'enregistrer une carte de crédit pour que les détails n'aient pas à être saisis de nouveau. Des sites comme Google Maps peuvent utiliser les emplacement enregistrés ou l'emplacement en cours pour fournir des informations d'itinéraire et chercher ou utiliser l'historique des voyages précédents pour trouver des boutiques locales dans les résultats de recherche.

+ +

Une analyse plus approfondie des habitudes des utilisateurs peut être utilisée pour anticiper leurs intérêts et personnaliser les réponses ou les notifications du serveur, par exemple pour fournir une liste des lieux précédemment visités ou les plus populaires que vous pourriez vouloir chercher sur la carte.

+ +
+

Note : Google Maps sauvegarde vos recherches et votre historique de visites. Les emplacement fréquemment visités ou fréquemment recherchés sont plus mis en avant que les autres.

+ +

Les résultats de recherche Google sont optimisés en fonction des recherches précédentes.

+ +
    +
  1.  Allez sur Google.
  2. +
  3.  Recherchez "football".
  4. +
  5.  Maintenant tapez "favoris" dans la barre de recherche et regardez les prédictions de recherche de l'autocomplete.
  6. +
+ +

Coïncidence ? Nada!

+
+ +

Accès contrôlé au contenu

+ +

La programmation côté serveur permet aux sites de restreindre l'accès aux utilisateurs autorisés et de ne servir que les informations qu'un utilisateur à la permission de voir.

+ +

Quelques exemples du monde réel incluent :

+ + + +
+

Note : Il existe de nombreux autres exemples où l'accès au contenu est contrôlé. Par exemple, que voyez-vous si vous allez sur le site en ligne de votre banque ? Connectez-vous à votre compte — quelles autres informations pouvez-vous voir et modifier ? Quelles informations pouvez-vous voir que seule la banque peut changer ?

+
+ +

Stocker les informations de session/d'état

+ +

La programmation côté serveur permet aux développeurs d'utiliser des sessions — en gros, un mécanisme qui permet au serveur de stocker des informations sur l'utilisation en cours d'un site et d'envoyer des réponses différentes selon cette information.

+ +

Cela permet, par exemple, à un site de savoir qu'un utilisateur s'est déjà identifié et afficher des messages qui lui sont destinés, d'afficher son historique de commande, ou peut-être encore, dans le cas d'un jeu, lui permettre de reprendre là où il en est resté.

+ +
+

Note : Visitez le site d'un journal qui a une offre d'abonnement et ouvrez des pages (par exemple The Age). Si vous continuez à visiter le site quelques heures/jours, éventuellement, vous commencerez à être redirigé vers des pages expliquant comment vous abonner, et vous ne pourrez plus accéder aux articles. Cette information est un exemple de session stockée dans des cookies.

+
+ +

Notifications et communication

+ +

Les serveurs peuvent envoyer des notifications générales ou personnalisées à l'utilisateur via le site web lui-même ou par email, SMS, messagerie instantannée, conversations vidéo ou autres services de communication.

+ +

Quelques exemples incluent :

+ + + +
+

Note : Le type de notification le plus courant est la "confirmation d'inscription". Choisissez presque n'importe quel site qui vous intéresse (Google, Amazon, Instagram, etc.) et créez un nouveau compte en utilisant votre adresse email. Vous recevrez rapidement un email qui confirme votre inscription, ou qui exige une confirmation pour activer votre compte.

+
+ +

Analyse des données

+ +

Un site web peut collecter beaucoup de données sur les utilisateurs : ce qu'ils cherchent, ce qu'ils achètent, ce qu'ils recommandent, combien de temps ils restent sur chaque page. La programmation côté serveur peut être utilisée pour affiner les réponses en fonction de l'analyse de ces données.

+ +

Par exemple, Amazon et Google font tous deux de la publicité pour des produits en se basant sur les recherches précédentes, sur les achats que vous avez faits.

+ +
+

Note : Si vous êtes un utilisateur de Facebook, allez sur votre flux principal et regardez les posts. Notez que certains posts ne sont pas classés par ordre numérique — en particulier, les posts qui ont le plus de "likes" sont souvent placés plus haut dans la liste que les posts plus récents.

+ +

Observez également quels types de publicités vous voyez — vous pourrez voir des publicités pour des choses que vous avez regardé sur d'autres sites. L'algorithme de Facebook pour mettre en avant du contenu et faire de la publicité est un peu un mystérieux, mais il est clair qu'il prend en compte vos likes et ce que vous avez l'habitude de regarder !

+
+ +

Sommaire

+ +

Félicitations, vous avez atteint la fin du premier article sur la programmation côté serveur. 

+ +

Vous avez maintenant appris que le code côté serveur est exécuté sur un serveur web et que son rôle principal est de contrôler quelle information est envoyée à l'utilisateur (tandis que le code côté client gère principalement la structure et la présentation des données pour l'utilisateur).

+ +

Vous devez également comprendre que c'est utile pour créer des sites web qui délivrent de l'information efficacement, adaptée à chaque utilisateur et avoir une bonne idée de quelques choses que vous seriez capable de faire quand vous serez programmeur back-end.

+ +

Finalement, vous devez comprendre que le code côté serveur peut être écrit dans de nombreux langages de programmation et que l'on peut utiliser des frameworks web pour rendre ce processus plus facile.

+ +

Dans un futur article,  nous vous aiderons à choisir le framework le plus adapté pour la création d'un premier site. Ensuite, nous vous présenterons les principales interactions client-serveur plus en détails.

+ +

{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}

+ +

Dans ce module

+ + diff --git a/files/fr/learn/server-side/first_steps/web_frameworks/index.html b/files/fr/learn/server-side/first_steps/web_frameworks/index.html deleted file mode 100644 index ca3f14e2ff..0000000000 --- a/files/fr/learn/server-side/first_steps/web_frameworks/index.html +++ /dev/null @@ -1,307 +0,0 @@ ---- -title: Les frameworks web côté serveur -slug: Learn/Server-side/First_steps/Web_frameworks -tags: - - Débutant - - Guide - - Server - - Web -translation_of: Learn/Server-side/First_steps/Web_frameworks -original_slug: Learn/Server-side/Premiers_pas/Web_frameworks ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}
- -

L'article précédent nous a montré à quoi ressemble la communication entre les clients et les serveurs web, la nature des demandes et des réponses HTTP et ce qu’une application web côté serveur doit faire pour répondre aux demandes d’un navigateur web. Avec ces connaissances en main, il est temps d'explorer comment les frameworks peuvent nous simplifier la tâche.  En chemin, vous comprendrez comment choisir le framework le mieux adapté pour votre première application web côté serveur.

- - - - - - - - - - - - -
Prérequis :Connaissance informatique de base.
Objectif :Comprendre comment les frameworks simplifient le développement/la maintenance du code côté serveur et vous faire réfléchir à propos de la sélection d'un framework pour votre propre développement.
- -

Les sections suivantes proposent des illustrations avec des fragments de codes utilisés par des frameworks réels. Ne soyez pas inquiété si vous ne comprenez pas tout directement. Les explications viendront au fur et à mesure.

- -

Vue d'ensemble

- -

Les frameworks web côté serveur (c-à-d "web application frameworks") facilitent l'écriture, la maintenance et la mise à l'échelle d'applications web. Ils fournissent des outils et des bibliothèques qui simplifient les tâches courantes de développement web, notamment l'acheminement des URL vers les gestionnaires appropriés, l'interaction avec les bases de données, les sessions et l'autorisation des utilisateurs, le formatage de la sortie (HTML, JSON, XML, par exemple) et l'amélioration de la sécurité contre les attaques web.

- -

Dans la section suivante, nous allons voir un peu plus de détails comment les frameworks facilite le développement d'applications web. Nous verrons alors les critères utilisés pour choisir un framework adapté.

- -

Que peut faire un framework web pour vous ?

- -

Les frameworks web fournissent des outils et des bibliothèques pour simplifier les opérations de développement web courantes. Utiliser un framework web côté serveur n'est pas obligatoire, mais fortement conseillé... Cela vous facilitera grandement la vie. Cette section présente certaines des fonctionnalités parmi les plus souvent fournies par les frameworks web.

- -

Travailler directement avec les requêtes et les réponses HTTP

- -

Comme nous l'avons vu dans le dernier article, les serveurs web et les navigateurs communiquent via le protocole HTTP — les serveurs attendent les requêtes HTTP du navigateur, puis renvoient des informations dans les réponses HTTP. Les infrastructures web vous permettent d'écrire une syntaxe simplifiée qui générera un code côté serveur pour travailler avec ces demandes et réponses. Cela signifie que vous aurez un travail plus facile, en interagissant avec un code plus facile, de niveau supérieur, plutôt que des primitives de réseau de niveau inférieur.

- -

L'exemple ci-dessous montre comment cela fonctionne dans le framework web Django (Python). Chaque fonction "view" (un gestionnaire de demandes) reçoit un objet HttpRequest contenant les informations de la demande et doit renvoyer un objet HttpResponse avec la sortie formatée (dans ce cas une chaîne).

- -
# Django view function
-from django.http import HttpResponse
-
-def index(request):
-    # Get an HttpRequest (request)
-    # perform operations using information from the request.
-    # Return HttpResponse
-    return HttpResponse('Output string to return')
-
- -

Acheminer les requettes au gestionnaire approprié

- -

La plupart des sites fournissent un certain nombre de ressources différentes, accessibles via des URL distinctes. Il est difficile de gérer toutes ces fonctions en une seule fois. Par conséquent, les infrastructures web fournissent des mécanismes simples pour mapper les modèles d'URL vers des fonctions de gestionnaire spécifiques. Cette approche présente également des avantages en termes de maintenance, car vous pouvez modifier l'URL utilisée pour fournir une fonctionnalité particulière sans avoir à modifier le code sous-jacent.

- -

Différents frameworks utilisent différents mécanismes pour la cartographie. Par exemple, l'infrastructure web Flask (Python) ajoute des itinéraires pour afficher les fonctions à l'aide d'un décorateur.

- -
@app.route("/")
-def hello():
-    return "Hello World!"
- -

Alors que Django attend des développeurs qu'ils définissent une liste de mappages d'URL entre un modèle d'URL et une fonction d'affichage.

- -
urlpatterns = [
-    url(r'^$', views.index),
-    # example: /best/myteamname/5/
-    url(r'^(?P<team_name>\w.+?)/(?P<team_number>[0-9]+)/$', views.best),
-]
-
- -

Faciliter l'accès aux données via la requête

- -

Les données peuvent être encodées dans une requête HTTP de différentes manières. Une demande HTTP GET pour obtenir des fichiers ou des données du serveur peut coder les données requises dans les paramètres d'URL ou dans la structure d'URL. Une demande HTTP POST de mise à jour une ressource sur le serveur inclura plutôt les informations de mise à jour en tant que "données POST" dans le corps de la demande. La requête HTTP peut également inclure des informations sur la session ou l'utilisateur en cours dans un cookie côté client. Les frameworks web fournissent des mécanismes appropriés au langage de programmation pour accéder à ces informations. Par exemple, l'objet HttpRequest que Django transmet à chaque fonction d'affichage contient des méthodes et des propriétés permettant d'accéder à l'URL cible, le type de demande (par exemple, un HTTP GET), les paramètres GET ou POST, les données de cookie et de session, etc. Django peut également transmettre informations codées dans la structure de l'URL en définissant des "modèles de capture" dans le mappeur d'URL (voir le dernier fragment de code dans la section ci-dessus).

- -

extraction et simplification de l’accès à la base de données

- -

Les sites web utilisent des bases de données pour stocker des informations à partager avec les utilisateurs et sur les utilisateurs. Les infrastructures web fournissent souvent une couche de base de données qui extrait les opérations de lecture, d'écriture, de requête et de suppression de base de données. Cette couche d'extraction est appelée ORM (Object-Relational Mapper).

- -

L'utilisation d'un ORM présente deux avantages :   

- -
    -
  1. Vous pouvez remplacer la base de données sous-jacente sans avoir nécessairement besoin de changer le code qui l'utilise. Cela permet aux développeurs d’optimiser les caractéristiques des différentes bases de données en fonction de leur utilisation.   
  2. -
  3. La validation de base des données peut être mise en œuvre avec le framework. Il est ainsi plus facile et plus sûr de vérifier que les données sont stockées dans le type de champ de base de données approprié, qu’elles ont le format correct (par exemple une adresse électronique) et qu’elles ne sont en aucun cas malveillantes (les pirates peuvent utiliser certains modèles de code pour agir mal, telles que la suppression des enregistrements de la base de données).
  4. -
- -

Par exemple, le framework web Django fournit un ORM et fait référence à l'objet utilisé pour définir la structure d'un enregistrement en tant que modèle. Le modèle spécifie les types de champs à stocker, ce qui peut fournir une validation au niveau du champ sur les informations pouvant être stockées (par exemple, un champ de courrier électronique autoriserait uniquement les adresses de courrier électronique valides). Les définitions de champ peuvent également spécifier leur taille maximale, leurs valeurs par défaut, leurs options de liste de sélection, leur aide pour la documentation, leur libellé pour les formulaires, etc. Le modèle ne spécifie aucune information sur la base de données sous-jacente, il s'agit d'un paramètre de configuration susceptible d'être modifié séparément de notre code.

- -

Le premier extrait de code ci-dessous montre un modèle Django très simple pour un objet Team. Ceci stocke le nom et le niveau de l'équipe en tant que champs de caractères et spécifie un nombre maximal de caractères à stocker pour chaque enregistrement. Team_level étant un champ de choix, nous fournissons également un mappage entre les choix à afficher et les données à stocker, ainsi qu'une valeur par défaut.

- -
#best/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'),
-        ...  #list our other teams
-    )
-    team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
-
- -

Le modèle Django fournit une API de requête simple pour la recherche dans la base de données. Cela peut correspondre à plusieurs champs à la fois en utilisant différents critères (par exemple, exact, insensible à la casse, supérieur à, etc.), et peut prendre en charge des instructions complexes (par exemple, vous pouvez spécifier une recherche sur les équipes U11 ayant un nom d'equipe (team name) qui commence par "Fr" ou se termine par "al").

- -

Le deuxième extrait de code montre une fonction d'affichage (gestionnaire de ressources) permettant d'afficher toutes nos équipes U09. Dans ce cas, nous spécifions que nous voulons filtrer tous les enregistrements où le champ team_level a exactement le texte 'U09' (notez dans l exemple ci dessous comment ce critère est transmis à la fonction filter () sous forme d'argument avec le nom du champ et le type de correspondance séparés par un double. underscores: team_level__exact).

- -
#best/views.py
-
-from django.shortcuts import render
-from .models import Team
-
-def youngest(request):
-    list_teams = Team.objects.filter(team_level__exact="U09")
-    context = {'youngest_teams': list_teams}
-    return render(request, 'best/index.html', context)
-
- -
-
- -

Rendering data

- -

Les frameworks web fournissent souvent des systèmes de templates. Ceux-ci vous permettent de spécifier la structure d'un document de sortie, en utilisant des espaces réservés pour les données qui seront ajoutées lors de la génération d'une page. Les modèles sont souvent utilisés pour créer du HTML, mais peuvent également créer d'autres types de documents.

- -

Les frameworks web fournissent souvent un mécanisme facilitant la génération d'autres formats à partir de données stockées, notamment {{glossary ("JSON")}} et {{glossary ("XML")}}.

- -

Par exemple, le système de templates Django vous permet de spécifier des variables en utilisant une syntaxe "double-handlebars" (par exemple,{{ variable_name }} ), qui sera remplacée par les valeurs transmises à partir de la fonction d'affichage lors du rendu d'une page. Le système de templates prend également en charge les expressions (avec la syntaxe:{% expression %} ), qui permettent aux templates d'effectuer des opérations simples, telles que l'itération des valeurs de liste transmises au modèle.

- -
-

Note : Many other templating systems use a similar syntax, e.g.: Jinja2 (Python), handlebars (JavaScript), moustache (JavaScript), etc.

-
- -

L'extrait de code ci-dessous montre comment cela fonctionne. En reprenant l'exemple "youngest_teams" de la section précédente, le modèle HTML se voit transmettre par la vue une variable de liste nommée youngest_teams. Dans le squelette HTML, nous avons une expression qui vérifie d'abord si la variable youngest_teams existe, puis itère dans une boucle for. À chaque itération, le modèle affiche la valeur team_name de l'équipe dans un élément de liste.

- -
#best/templates/best/index.html
-
-<!DOCTYPE html>
-<html lang="en">
-<body>
-
- {% if youngest_teams %}
-    <ul>
-    {% for team in youngest_teams %}
-        <li>\{\{ team.team_name \}\}</li>
-    {% endfor %}
-    </ul>
-{% else %}
-    <p>No teams are available.</p>
-{% endif %}
-
-</body>
-</html>
-
- -

Comment choisir un framework web

- -

Il existe de nombreux frameworks web pour presque tous les langages de programmation que vous souhaitez utiliser (nous énumérons quelques-uns des frameworks les plus populaires dans la section suivante). Avec autant de choix, il peut devenir difficile de déterminer quel framework constitue le meilleur point de départ pour votre nouvelle application web.

- -

Certains des facteurs qui peuvent affecter votre décision sont les suivants:

- - - -

Il existe de nombreux autres facteurs possibles, y compris les licences, que le cadre soit ou non en cours de développement actif, etc.

- -

Si vous débutez en programmation, vous choisirez probablement un framework facile à prendre en main. Une documentation riche et une communauté active sont également des critères pertinents pour votre choix. Pour la suite de ce cours, nous avons choisi Django (Python) et Express (Node/JavaScript) principalement parce que ces frameworks sont faciles à apprendre et bénéficient d'un bon soutien.

- -
-

Note : Let's go to the main websites for Django (Python) and Express (Node/JavaScript) and check out their documentation and community.

- -
    -
  1. Navigate to the main sites (linked above) -
      -
    • Click on the Documentation menu links (named things like "Documentation, Guide, API Reference, Getting Started".
    • -
    • Can you see topics showing how to set up URL routing, templates, and databases/models?
    • -
    • Are the documents clear?
    • -
    -
  2. -
  3. Navigate to mailing lists for each site (accessible from Community links). -
      -
    • How many questions have been posted in the last few days
    • -
    • How many have responses?
    • -
    • Do they have an active community?
    • -
    -
  4. -
-
- -

A few good web frameworks?

- -

Let's now move on, and discuss a few specific server-side web frameworks.

- -

The server-side frameworks below represent a few of the most popular available at the time of writing. All of them have everything you need to be productive — they are open source, are under active development, have enthusiastic communities creating documentation and helping users on discussion boards, and are used in large numbers of high-profile websites. There are many other great server-side frameworks that you can discover using a basic internet search.

- -
-

Note : Descriptions come (partially) from the framework websites!

-
- -

Django (Python)

- -

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.

- -

Django follows the "Batteries included" philosophy and provides almost everything most developers might want to do "out of the box". Because everything is included, it all works together, follows consistent design principles, and has extensive and up-to-date documentation. It is also fast, secure, and very scalable. Being based on Python, Django code is easy to read and to maintain.

- -

Popular sites using Django (from Django home page) include: Disqus, Instagram, Knight Foundation, MacArthur Foundation, Mozilla, National Geographic, Open Knowledge Foundation, Pinterest, Open Stack.

- -

Flask (Python)

- -

Flask is a microframework for Python.

- -

While minimalist, Flask can create serious websites out of the box. It contains a development server and debugger, and includes support for Jinja2 templating, secure cookies, unit testing, and RESTful request dispatching. It has good documentation and an active community.

- -

Flask has become extremely popular, particularly for developers who need to provide web services on small, resource-constrained systems (e.g. running a web server on a Raspberry Pi, Drone controllers, etc.)

- -

Express (Node.js/JavaScript)

- -

Express is a fast, unopinionated, flexible and minimalist web framework for Node.js (node is a browserless environment for running JavaScript). It provides a robust set of features for web and mobile applications and delivers useful HTTP utility methods and middleware.

- -

Express is extremely popular, partially because it eases the migration of client-side JavaScript web programmers into server-side development, and partially because it is resource-efficient (the underlying node environment uses lightweight multitasking within a thread rather than spawning separate processes for every new web request).

- -

Because Express is a minimalist web framework it does not incorporate every component that you might want to use (for example, database access and support for users and sessions are provided through independent libraries). There are many excellent independent components, but sometimes it can be hard to work out which is the best for a particular purpose!

- -

Many popular server-side and full stack frameworks (comprising both server and client-side frameworks) are based on Express, including Feathers, ItemsAPI, KeystoneJS, Kraken, LoopBack, MEAN, and Sails.

- -

A lot of high profile companies use Express, including: Uber, Accenture, IBM, etc. (a list is provided here).

- -

Ruby on Rails (Ruby)

- -

Rails (usually referred to as "Ruby on Rails") is a web framework written for the Ruby programming language.

- -

Rails follows a very similar design philosophy to Django. Like Django it provides standard mechanisms for routing URLs, accessing data from a database, generating HTML from templates and formatting data as {{glossary("JSON")}} or {{glossary("XML")}}. It similarly encourages the use of design patterns like DRY ("dont repeat yourself" — write code only once if at all possible), MVC (model-view-controller) and a number of others.

- -

There are of course many differences due to specific design decisions and the nature of the languages.

- -

Rails has been used for high profile sites, including: Basecamp, GitHub, Shopify, Airbnb, Twitch, SoundCloud, Hulu, Zendesk, Square, Highrise.

- -

Laravel (PHP)

- -

Laravel is a web application framework with expressive, elegant syntax. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as:

- - - -

Laravel is accessible, yet powerful, providing tools needed for large, robust applications.

- -

ASP.NET

- -

ASP.NET is an open source web framework developed by Microsoft for building modern web applications and services. With ASP.NET you can quickly create web sites based on HTML, CSS, and JavaScript, scale them for use by millions of users and easily add more complex capabilities like Web APIs, forms over data, or real time communications.

- -

One of the differentiators for ASP.NET is that it is built on the Common Language Runtime (CLR), allowing programmers to write ASP.NET code using any supported .NET language (C#, Visual Basic, etc.). Like many Microsoft products it benefits from excellent tools (often free), an active developer community, and well-written documentation.

- -

ASP.NET is used by Microsoft, Xbox.com, Stack Overflow, and many others.

- -

Mojolicious (Perl)

- -

Mojolicious is a next generation web framework for the Perl programming language.

- -

Back in the early days of the web, many people learned Perl because of a wonderful Perl library called CGI. It was simple enough to get started without knowing much about the language and powerful enough to keep you going. Mojolicious implements this idea using bleeding edge technologies.

- -

Some of the features provided by Mojolicious are: Real-time web framework, to easily grow single file prototypes into well-structured MVC web applications; RESTful routes, plugins, commands, Perl-ish templates, content negotiation, session management, form validation, testing framework, static file server, CGI/PSGI detection, first class Unicode support; Full stack HTTP and WebSocket client/server implementation with IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, UNIX domain socket, Comet (long polling), keep-alive, connection pooling, timeout, cookie, multipart and gzip compression support; JSON and HTML/XML parsers and generators with CSS selector support; Very clean, portable and object-oriented pure-Perl API with no hidden magic; Fresh code based upon years of experience, free and open source.

- -

Summary

- -

This article has shown that web frameworks can make it easier to develop and maintain server-side code. It has also provided a high level overview of a few popular frameworks, and discussed criteria for choosing a web application framework. You should now have at least an idea of how to choose a web framework for your own server-side development. If not, then don't worry — later on in the course we'll give you detailed tutorials on Django and Express to give you some experience of actually working with a web framework.

- -

For the next article in this module we'll change direction slightly and consider web security.

- -

{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}

- -

In this module

- - diff --git a/files/fr/learn/server-side/first_steps/web_frameworks/index.md b/files/fr/learn/server-side/first_steps/web_frameworks/index.md new file mode 100644 index 0000000000..ca3f14e2ff --- /dev/null +++ b/files/fr/learn/server-side/first_steps/web_frameworks/index.md @@ -0,0 +1,307 @@ +--- +title: Les frameworks web côté serveur +slug: Learn/Server-side/First_steps/Web_frameworks +tags: + - Débutant + - Guide + - Server + - Web +translation_of: Learn/Server-side/First_steps/Web_frameworks +original_slug: Learn/Server-side/Premiers_pas/Web_frameworks +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}
+ +

L'article précédent nous a montré à quoi ressemble la communication entre les clients et les serveurs web, la nature des demandes et des réponses HTTP et ce qu’une application web côté serveur doit faire pour répondre aux demandes d’un navigateur web. Avec ces connaissances en main, il est temps d'explorer comment les frameworks peuvent nous simplifier la tâche.  En chemin, vous comprendrez comment choisir le framework le mieux adapté pour votre première application web côté serveur.

+ + + + + + + + + + + + +
Prérequis :Connaissance informatique de base.
Objectif :Comprendre comment les frameworks simplifient le développement/la maintenance du code côté serveur et vous faire réfléchir à propos de la sélection d'un framework pour votre propre développement.
+ +

Les sections suivantes proposent des illustrations avec des fragments de codes utilisés par des frameworks réels. Ne soyez pas inquiété si vous ne comprenez pas tout directement. Les explications viendront au fur et à mesure.

+ +

Vue d'ensemble

+ +

Les frameworks web côté serveur (c-à-d "web application frameworks") facilitent l'écriture, la maintenance et la mise à l'échelle d'applications web. Ils fournissent des outils et des bibliothèques qui simplifient les tâches courantes de développement web, notamment l'acheminement des URL vers les gestionnaires appropriés, l'interaction avec les bases de données, les sessions et l'autorisation des utilisateurs, le formatage de la sortie (HTML, JSON, XML, par exemple) et l'amélioration de la sécurité contre les attaques web.

+ +

Dans la section suivante, nous allons voir un peu plus de détails comment les frameworks facilite le développement d'applications web. Nous verrons alors les critères utilisés pour choisir un framework adapté.

+ +

Que peut faire un framework web pour vous ?

+ +

Les frameworks web fournissent des outils et des bibliothèques pour simplifier les opérations de développement web courantes. Utiliser un framework web côté serveur n'est pas obligatoire, mais fortement conseillé... Cela vous facilitera grandement la vie. Cette section présente certaines des fonctionnalités parmi les plus souvent fournies par les frameworks web.

+ +

Travailler directement avec les requêtes et les réponses HTTP

+ +

Comme nous l'avons vu dans le dernier article, les serveurs web et les navigateurs communiquent via le protocole HTTP — les serveurs attendent les requêtes HTTP du navigateur, puis renvoient des informations dans les réponses HTTP. Les infrastructures web vous permettent d'écrire une syntaxe simplifiée qui générera un code côté serveur pour travailler avec ces demandes et réponses. Cela signifie que vous aurez un travail plus facile, en interagissant avec un code plus facile, de niveau supérieur, plutôt que des primitives de réseau de niveau inférieur.

+ +

L'exemple ci-dessous montre comment cela fonctionne dans le framework web Django (Python). Chaque fonction "view" (un gestionnaire de demandes) reçoit un objet HttpRequest contenant les informations de la demande et doit renvoyer un objet HttpResponse avec la sortie formatée (dans ce cas une chaîne).

+ +
# Django view function
+from django.http import HttpResponse
+
+def index(request):
+    # Get an HttpRequest (request)
+    # perform operations using information from the request.
+    # Return HttpResponse
+    return HttpResponse('Output string to return')
+
+ +

Acheminer les requettes au gestionnaire approprié

+ +

La plupart des sites fournissent un certain nombre de ressources différentes, accessibles via des URL distinctes. Il est difficile de gérer toutes ces fonctions en une seule fois. Par conséquent, les infrastructures web fournissent des mécanismes simples pour mapper les modèles d'URL vers des fonctions de gestionnaire spécifiques. Cette approche présente également des avantages en termes de maintenance, car vous pouvez modifier l'URL utilisée pour fournir une fonctionnalité particulière sans avoir à modifier le code sous-jacent.

+ +

Différents frameworks utilisent différents mécanismes pour la cartographie. Par exemple, l'infrastructure web Flask (Python) ajoute des itinéraires pour afficher les fonctions à l'aide d'un décorateur.

+ +
@app.route("/")
+def hello():
+    return "Hello World!"
+ +

Alors que Django attend des développeurs qu'ils définissent une liste de mappages d'URL entre un modèle d'URL et une fonction d'affichage.

+ +
urlpatterns = [
+    url(r'^$', views.index),
+    # example: /best/myteamname/5/
+    url(r'^(?P<team_name>\w.+?)/(?P<team_number>[0-9]+)/$', views.best),
+]
+
+ +

Faciliter l'accès aux données via la requête

+ +

Les données peuvent être encodées dans une requête HTTP de différentes manières. Une demande HTTP GET pour obtenir des fichiers ou des données du serveur peut coder les données requises dans les paramètres d'URL ou dans la structure d'URL. Une demande HTTP POST de mise à jour une ressource sur le serveur inclura plutôt les informations de mise à jour en tant que "données POST" dans le corps de la demande. La requête HTTP peut également inclure des informations sur la session ou l'utilisateur en cours dans un cookie côté client. Les frameworks web fournissent des mécanismes appropriés au langage de programmation pour accéder à ces informations. Par exemple, l'objet HttpRequest que Django transmet à chaque fonction d'affichage contient des méthodes et des propriétés permettant d'accéder à l'URL cible, le type de demande (par exemple, un HTTP GET), les paramètres GET ou POST, les données de cookie et de session, etc. Django peut également transmettre informations codées dans la structure de l'URL en définissant des "modèles de capture" dans le mappeur d'URL (voir le dernier fragment de code dans la section ci-dessus).

+ +

extraction et simplification de l’accès à la base de données

+ +

Les sites web utilisent des bases de données pour stocker des informations à partager avec les utilisateurs et sur les utilisateurs. Les infrastructures web fournissent souvent une couche de base de données qui extrait les opérations de lecture, d'écriture, de requête et de suppression de base de données. Cette couche d'extraction est appelée ORM (Object-Relational Mapper).

+ +

L'utilisation d'un ORM présente deux avantages :   

+ +
    +
  1. Vous pouvez remplacer la base de données sous-jacente sans avoir nécessairement besoin de changer le code qui l'utilise. Cela permet aux développeurs d’optimiser les caractéristiques des différentes bases de données en fonction de leur utilisation.   
  2. +
  3. La validation de base des données peut être mise en œuvre avec le framework. Il est ainsi plus facile et plus sûr de vérifier que les données sont stockées dans le type de champ de base de données approprié, qu’elles ont le format correct (par exemple une adresse électronique) et qu’elles ne sont en aucun cas malveillantes (les pirates peuvent utiliser certains modèles de code pour agir mal, telles que la suppression des enregistrements de la base de données).
  4. +
+ +

Par exemple, le framework web Django fournit un ORM et fait référence à l'objet utilisé pour définir la structure d'un enregistrement en tant que modèle. Le modèle spécifie les types de champs à stocker, ce qui peut fournir une validation au niveau du champ sur les informations pouvant être stockées (par exemple, un champ de courrier électronique autoriserait uniquement les adresses de courrier électronique valides). Les définitions de champ peuvent également spécifier leur taille maximale, leurs valeurs par défaut, leurs options de liste de sélection, leur aide pour la documentation, leur libellé pour les formulaires, etc. Le modèle ne spécifie aucune information sur la base de données sous-jacente, il s'agit d'un paramètre de configuration susceptible d'être modifié séparément de notre code.

+ +

Le premier extrait de code ci-dessous montre un modèle Django très simple pour un objet Team. Ceci stocke le nom et le niveau de l'équipe en tant que champs de caractères et spécifie un nombre maximal de caractères à stocker pour chaque enregistrement. Team_level étant un champ de choix, nous fournissons également un mappage entre les choix à afficher et les données à stocker, ainsi qu'une valeur par défaut.

+ +
#best/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'),
+        ...  #list our other teams
+    )
+    team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
+
+ +

Le modèle Django fournit une API de requête simple pour la recherche dans la base de données. Cela peut correspondre à plusieurs champs à la fois en utilisant différents critères (par exemple, exact, insensible à la casse, supérieur à, etc.), et peut prendre en charge des instructions complexes (par exemple, vous pouvez spécifier une recherche sur les équipes U11 ayant un nom d'equipe (team name) qui commence par "Fr" ou se termine par "al").

+ +

Le deuxième extrait de code montre une fonction d'affichage (gestionnaire de ressources) permettant d'afficher toutes nos équipes U09. Dans ce cas, nous spécifions que nous voulons filtrer tous les enregistrements où le champ team_level a exactement le texte 'U09' (notez dans l exemple ci dessous comment ce critère est transmis à la fonction filter () sous forme d'argument avec le nom du champ et le type de correspondance séparés par un double. underscores: team_level__exact).

+ +
#best/views.py
+
+from django.shortcuts import render
+from .models import Team
+
+def youngest(request):
+    list_teams = Team.objects.filter(team_level__exact="U09")
+    context = {'youngest_teams': list_teams}
+    return render(request, 'best/index.html', context)
+
+ +
+
+ +

Rendering data

+ +

Les frameworks web fournissent souvent des systèmes de templates. Ceux-ci vous permettent de spécifier la structure d'un document de sortie, en utilisant des espaces réservés pour les données qui seront ajoutées lors de la génération d'une page. Les modèles sont souvent utilisés pour créer du HTML, mais peuvent également créer d'autres types de documents.

+ +

Les frameworks web fournissent souvent un mécanisme facilitant la génération d'autres formats à partir de données stockées, notamment {{glossary ("JSON")}} et {{glossary ("XML")}}.

+ +

Par exemple, le système de templates Django vous permet de spécifier des variables en utilisant une syntaxe "double-handlebars" (par exemple,{{ variable_name }} ), qui sera remplacée par les valeurs transmises à partir de la fonction d'affichage lors du rendu d'une page. Le système de templates prend également en charge les expressions (avec la syntaxe:{% expression %} ), qui permettent aux templates d'effectuer des opérations simples, telles que l'itération des valeurs de liste transmises au modèle.

+ +
+

Note : Many other templating systems use a similar syntax, e.g.: Jinja2 (Python), handlebars (JavaScript), moustache (JavaScript), etc.

+
+ +

L'extrait de code ci-dessous montre comment cela fonctionne. En reprenant l'exemple "youngest_teams" de la section précédente, le modèle HTML se voit transmettre par la vue une variable de liste nommée youngest_teams. Dans le squelette HTML, nous avons une expression qui vérifie d'abord si la variable youngest_teams existe, puis itère dans une boucle for. À chaque itération, le modèle affiche la valeur team_name de l'équipe dans un élément de liste.

+ +
#best/templates/best/index.html
+
+<!DOCTYPE html>
+<html lang="en">
+<body>
+
+ {% if youngest_teams %}
+    <ul>
+    {% for team in youngest_teams %}
+        <li>\{\{ team.team_name \}\}</li>
+    {% endfor %}
+    </ul>
+{% else %}
+    <p>No teams are available.</p>
+{% endif %}
+
+</body>
+</html>
+
+ +

Comment choisir un framework web

+ +

Il existe de nombreux frameworks web pour presque tous les langages de programmation que vous souhaitez utiliser (nous énumérons quelques-uns des frameworks les plus populaires dans la section suivante). Avec autant de choix, il peut devenir difficile de déterminer quel framework constitue le meilleur point de départ pour votre nouvelle application web.

+ +

Certains des facteurs qui peuvent affecter votre décision sont les suivants:

+ + + +

Il existe de nombreux autres facteurs possibles, y compris les licences, que le cadre soit ou non en cours de développement actif, etc.

+ +

Si vous débutez en programmation, vous choisirez probablement un framework facile à prendre en main. Une documentation riche et une communauté active sont également des critères pertinents pour votre choix. Pour la suite de ce cours, nous avons choisi Django (Python) et Express (Node/JavaScript) principalement parce que ces frameworks sont faciles à apprendre et bénéficient d'un bon soutien.

+ +
+

Note : Let's go to the main websites for Django (Python) and Express (Node/JavaScript) and check out their documentation and community.

+ +
    +
  1. Navigate to the main sites (linked above) +
      +
    • Click on the Documentation menu links (named things like "Documentation, Guide, API Reference, Getting Started".
    • +
    • Can you see topics showing how to set up URL routing, templates, and databases/models?
    • +
    • Are the documents clear?
    • +
    +
  2. +
  3. Navigate to mailing lists for each site (accessible from Community links). +
      +
    • How many questions have been posted in the last few days
    • +
    • How many have responses?
    • +
    • Do they have an active community?
    • +
    +
  4. +
+
+ +

A few good web frameworks?

+ +

Let's now move on, and discuss a few specific server-side web frameworks.

+ +

The server-side frameworks below represent a few of the most popular available at the time of writing. All of them have everything you need to be productive — they are open source, are under active development, have enthusiastic communities creating documentation and helping users on discussion boards, and are used in large numbers of high-profile websites. There are many other great server-side frameworks that you can discover using a basic internet search.

+ +
+

Note : Descriptions come (partially) from the framework websites!

+
+ +

Django (Python)

+ +

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.

+ +

Django follows the "Batteries included" philosophy and provides almost everything most developers might want to do "out of the box". Because everything is included, it all works together, follows consistent design principles, and has extensive and up-to-date documentation. It is also fast, secure, and very scalable. Being based on Python, Django code is easy to read and to maintain.

+ +

Popular sites using Django (from Django home page) include: Disqus, Instagram, Knight Foundation, MacArthur Foundation, Mozilla, National Geographic, Open Knowledge Foundation, Pinterest, Open Stack.

+ +

Flask (Python)

+ +

Flask is a microframework for Python.

+ +

While minimalist, Flask can create serious websites out of the box. It contains a development server and debugger, and includes support for Jinja2 templating, secure cookies, unit testing, and RESTful request dispatching. It has good documentation and an active community.

+ +

Flask has become extremely popular, particularly for developers who need to provide web services on small, resource-constrained systems (e.g. running a web server on a Raspberry Pi, Drone controllers, etc.)

+ +

Express (Node.js/JavaScript)

+ +

Express is a fast, unopinionated, flexible and minimalist web framework for Node.js (node is a browserless environment for running JavaScript). It provides a robust set of features for web and mobile applications and delivers useful HTTP utility methods and middleware.

+ +

Express is extremely popular, partially because it eases the migration of client-side JavaScript web programmers into server-side development, and partially because it is resource-efficient (the underlying node environment uses lightweight multitasking within a thread rather than spawning separate processes for every new web request).

+ +

Because Express is a minimalist web framework it does not incorporate every component that you might want to use (for example, database access and support for users and sessions are provided through independent libraries). There are many excellent independent components, but sometimes it can be hard to work out which is the best for a particular purpose!

+ +

Many popular server-side and full stack frameworks (comprising both server and client-side frameworks) are based on Express, including Feathers, ItemsAPI, KeystoneJS, Kraken, LoopBack, MEAN, and Sails.

+ +

A lot of high profile companies use Express, including: Uber, Accenture, IBM, etc. (a list is provided here).

+ +

Ruby on Rails (Ruby)

+ +

Rails (usually referred to as "Ruby on Rails") is a web framework written for the Ruby programming language.

+ +

Rails follows a very similar design philosophy to Django. Like Django it provides standard mechanisms for routing URLs, accessing data from a database, generating HTML from templates and formatting data as {{glossary("JSON")}} or {{glossary("XML")}}. It similarly encourages the use of design patterns like DRY ("dont repeat yourself" — write code only once if at all possible), MVC (model-view-controller) and a number of others.

+ +

There are of course many differences due to specific design decisions and the nature of the languages.

+ +

Rails has been used for high profile sites, including: Basecamp, GitHub, Shopify, Airbnb, Twitch, SoundCloud, Hulu, Zendesk, Square, Highrise.

+ +

Laravel (PHP)

+ +

Laravel is a web application framework with expressive, elegant syntax. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as:

+ + + +

Laravel is accessible, yet powerful, providing tools needed for large, robust applications.

+ +

ASP.NET

+ +

ASP.NET is an open source web framework developed by Microsoft for building modern web applications and services. With ASP.NET you can quickly create web sites based on HTML, CSS, and JavaScript, scale them for use by millions of users and easily add more complex capabilities like Web APIs, forms over data, or real time communications.

+ +

One of the differentiators for ASP.NET is that it is built on the Common Language Runtime (CLR), allowing programmers to write ASP.NET code using any supported .NET language (C#, Visual Basic, etc.). Like many Microsoft products it benefits from excellent tools (often free), an active developer community, and well-written documentation.

+ +

ASP.NET is used by Microsoft, Xbox.com, Stack Overflow, and many others.

+ +

Mojolicious (Perl)

+ +

Mojolicious is a next generation web framework for the Perl programming language.

+ +

Back in the early days of the web, many people learned Perl because of a wonderful Perl library called CGI. It was simple enough to get started without knowing much about the language and powerful enough to keep you going. Mojolicious implements this idea using bleeding edge technologies.

+ +

Some of the features provided by Mojolicious are: Real-time web framework, to easily grow single file prototypes into well-structured MVC web applications; RESTful routes, plugins, commands, Perl-ish templates, content negotiation, session management, form validation, testing framework, static file server, CGI/PSGI detection, first class Unicode support; Full stack HTTP and WebSocket client/server implementation with IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, UNIX domain socket, Comet (long polling), keep-alive, connection pooling, timeout, cookie, multipart and gzip compression support; JSON and HTML/XML parsers and generators with CSS selector support; Very clean, portable and object-oriented pure-Perl API with no hidden magic; Fresh code based upon years of experience, free and open source.

+ +

Summary

+ +

This article has shown that web frameworks can make it easier to develop and maintain server-side code. It has also provided a high level overview of a few popular frameworks, and discussed criteria for choosing a web application framework. You should now have at least an idea of how to choose a web framework for your own server-side development. If not, then don't worry — later on in the course we'll give you detailed tutorials on Django and Express to give you some experience of actually working with a web framework.

+ +

For the next article in this module we'll change direction slightly and consider web security.

+ +

{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}

+ +

In this module

+ + diff --git a/files/fr/learn/server-side/first_steps/website_security/index.html b/files/fr/learn/server-side/first_steps/website_security/index.html deleted file mode 100644 index 4b90e6303a..0000000000 --- a/files/fr/learn/server-side/first_steps/website_security/index.html +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: La sécurité d'un site Web -slug: Learn/Server-side/First_steps/Website_security -tags: - - Débutant - - Guide - - Sécurité - - Sécurité Web -translation_of: Learn/Server-side/First_steps/Website_security -original_slug: Learn/Server-side/Premiers_pas/Website_security ---- -
{{LearnSidebar}}
- -
{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/Premiers_pas")}}
- -

La sécurité d'un site web exige de la vigilance dans tous les aspects de sa conception et de son utilisation. Cet article d'introduction ne fera pas de vous un gourou de la sécurité des sites web, mais il vous aidera à comprendre d'où viennent les menaces et ce que vous pouvez faire pour renforcer votre application web contre les attaques les plus courantes.

- - - - - - - - - - - - -
Pré-requis :Connaissances de base en informatique.
Objectif :Comprendre les menaces les plus courantes à la sécurité des applications web et ce que vous pouvez faire pour réduire le risque de piratage de votre site.
- -

Qu'est-ce que la sécurité d'un site web?

- -

Internet est un endroit dangereux ! Fréquemment, nous entendons parler de sites web devenus indisponibles en raison d'attaques par déni de service, ou affichant des informations modifiées (et souvent préjudiciables) sur leurs pages d'accueil. Dans d'autres cas, très médiatisés, des millions de mots de passe, d'adresses électroniques et de détails sur des cartes de crédit sont divulgués au public, exposant les utilisateurs du site web à la fois à l'embarras personnel et au risque de pertes financières.

- -

L'objectif de la sécurité des sites web est de prévenir ces types d'attaques. Plus formellement, la sécurité des sites web est l'acte de protéger les sites web contre l'accès, l'utilisation, la modification, la destruction ou la perturbation non autorisées.

- -

La sécurité efficace d'un site web nécessite un effort de conception sur l'ensemble du site : dans votre application web, dans la configuration du serveur web, dans vos politiques de création et de renouvellement des mots de passe et dans le code côté-client. Bien que tout cela semble très inquiétant, la bonne nouvelle est que si vous utilisez un framework web côté serveur, il incluera certainement par défaut des mécanismes de défense solides et bien pensés contre un certain nombre des attaques les plus courantes. D'autres attaques peuvent être atténuées grâce à la configuration de votre serveur web, par exemple en activant HTTPS. Enfin, les outils d'analyse de vulnérabilité accessibles au public peuvent vous aider à découvrir d'éventuelles erreurs dans votre conception.

- -

Le reste de cet article détaille les menaces les plus courantes qui pèsent sur les sites web et quelques étapes simples pour protèger votre site.

- -
-

Note : ceci est un article d'introduction, conçu pour vous aider à réflechir à la sécurité de votre site web. Il n'est en rien exhaustif.

-
- -

Menaces visant la sécurité des sites web

- -

Cette section n'énumère que quelques-unes des menaces les plus courantes visant les sites web et la façon dont elles sont tenues à distance. À mesure que vous lisez, notez comment les menaces apparaissent lorsque l'application web fait confiance ou n'est pas assez méfiante vis-à-vis des données provenant du navigateur !

- -

Cross-Site Scripting (XSS)

- -

XSS est un terme utilisé pour décrire une classe d'attaque qui permet à l'attaquant d'injecter des scripts, exécutés côté-client, au travers du site web pour viser le navigateur web des autres utilisateurs. Comme le code injecté provient du site web le navigateur web le considère comme sûr, il peut de ce fait faire des choses comme transmettre le cookie d'authentification de l'utilisateur à l'attaquant. Une fois que l'attaquant obtient ce cookie il peut se connecter sur le site comme si il était l'utilisateur attaqué et peut faire tout ce que l'utilisateur pourrait faire. En fonction du site sur lequel l'attaque se produit, cela peut inclure l'accès aux détails de carte bancaire, les informations des contacts, la modification du mot de passe, etc.

- -
-

Note : les vulnérabilités XSS ont historiquement été les plus courantes.

-
- -

Il y a deux manières principales pour demander au site de retourner un script injecté vers un navigateur web — elles sont désignées en tant que vulnérabilités XSS réfléchie et persistante.

- - - -

La meilleur défense contre les vulnérabilités XSS est de supprimer ou désactiver toutes les balises qui peuvent potentiellement contenir des instructions pour exécuter du code. Pour HTML cela inclut les tags comme <script>, <object>, <embed>, et <link>.

- -
-

Il est nécessaire de traiter les données saisies par l'utilisateur pour être sûr qu'il ne puisse ni exécuter de scripts ni pertuber le fonctionnement normal du site (ce procédé est appelé input sanitization en anglais). De nombreux frameworks proposent par défaut cette vérification sur les entrées des formulaires.

-
- -

Injection SQL

- -

L'injection SQL est une vulnérabilité qui permet à un attaquant d'exécuter du code SQL frauduleux sur une base de données, permettant l'accès, la modification ou la suppression des données quelque soit le droit de l'utilisateur. Une attaque par injection réussie peut permettre l'usurpation d'un compte, la création d'un compte avec les droits administrateur, l'accès à toute les données du serveur, ou la modification / destruction des données pour le rendre inutilisable.

- -

Cette vulnérabilité est présente quand la saisie de l'utilisateur est transmise à une requête SQL sous-jacente qui peut modifier le sens de la requête. Par exemple, dans le code ci-dessous qui devrait lister l'ensemble des utilisateurs avec un nom particulier (userName) et qui provient d'un formulaire HTML:

- -
statement = "SELECT * FROM users WHERE name = '" + userName + "';"
- -

Si l'utilisateur entre un nom correct cela marchera comme voulu. Cependant un utilisateur malveillant peut changer complètement le sens de cette requête SQL pour obtenir la requête qui suit, simplement en ajoutant le texte, en gras ci dessous, en tant que nom, cette requête, modifiée, va créer une requête SQL valide qui va supprimer la table users et sélectionner toute les données de la table userinfo (révèlant les informations de chaque utilisateur). Tout cela est rendu possible à cause du début du texte injecté  ('a';) qui va complèter la requête d'origine (' est le symbole pour délimiter une chaine de texte en SQL).

- -
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
-
- -

Le moyen pour éviter ce type d'attaque est de s'assurer que toute saisie de l'utilisateur transmise à une requête SQL ne peut pas changer la nature de cette requête. Un moyen de faire cela est d'échapper tous les caractères de la saisie utilisateur quand ils ont un sens particulier en SQL.

- -
-

Note : la requête SQL considère le symbole ' comme le début et la fin d'une chaine de texte. En ajoutant le caractère \ nous allons "échapper" ce symbole, et dire à SQL de le traiter comme une simple partie de la chaîne de caractères.

-
- -

Dans la requête ci-dessous nous avons échappé le caractère '. Le SQL va donc interpréter la chaine complète (en gras) comme un nom (un nom étrange en effet, mais pas nuisible).

- -
SELECT * FROM users WHERE name = 'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t';
-
-
- -

Les frameworks web se chargent bien souvent d'échapper ces caractères à votre place. Django, par exemple, s'assure que toute saisie d'un utilisateur transmise au modèle est bien échappée.

- -
-

Note : Cette section s'inspire beaucoup des informations de Wikipedia ici.

-
- -

Falsification de requête inter-sites (CSRF)

- -

Les attaques CSRF permettent à un utilisateur malveillant d'éxécuter des actions à l'a ide des identifiants d'un autre utilisateur sans que cet utilisateur ne soit informé ou consentant.

- -

Ce type d'attaque s'explique mieux avec un exemple. John est l'utilisateur malveillant qui sait qu'un site particulier permet à des utilisateurs authentifiés d'envoyer de l'argent vers un compte particulier en utilisant des requêtes HTTP POST qui incluent le numéro de compte et le montant. John construit un formulaire qui inclut son numéro de compte et un montant dans des champs cachés (invisibles) et le transmet à un autre utilisateur du site (avec le bouton de validation déguisé en un lien vers un site "pour devenir riche".

- -

Si un utilisateur clique sur le bouton de validation, une requête HTTP POST, contenant les informations de transaction, va être transmise au serveur ainsi que le cookie que le navigateur web associe au site (l'ajout à la requête du cookie associé au site est le comportement normal du navigateur). Le serveur va vérifier le cookie d'authentification, et l'utiliser pour déterminer si l'utilisateur est ou n'est pas connecté et donc permet ou non la transaction.

- -

Au final tout utilisateur qui va cliquer sur le bouton de validation, alors qu'il sera connecté sur le site d'échange d'argent, va autoriser la transaction. John va devenir riche !

- -
-

Note : l'astuce ici est que John n'a pas besoin d'accéder aux cookies de l'utilisateur (ou à ses identifiants), le navigateur web stocke cette information et l'inclut automatiquement dans toute les requêtes destinées au serveur associé.

-
- -

Un moyen de prévenir ce type d'attaque est que le serveur demande que chaque requête POST possède un secret généré par le serveur et spécifique à l'utilisateur (le secret serait transmis par le serveur lors de l'envoi du formulaire de transaction). Cette approche empêche John de créer son propre formulaire car il n'est pas capable de connaitre le secret que le serveur founit à l'utilisateur. Même si il venait à trouver ce secret et créer un formulaire pour un utilisateur particulier, il ne pourrait pas utiliser ce formulaire pour attaquer d'autres utilisateurs

- -

Les framework web incluent souvent des mécanismes afin de prévenir les attaques CSRF.

- -

Autre menaces

- -

Les autres attaques et vulnérabilités courantes incluent:

- - - -

Il y en a beaucoup d'autres. Pour une liste plus exhaustive vous pouvez consulter la catégorie failles de sécurité Web (Wikipedia) et la catégorie Attaques (du projet OWASP).

- -

Quelques messages clés

- -

La majorité des attaques citées précédement réusissent lorsque l'application web fait confiance aux données provenant du navigateur web. Quoique vous fassiez d'autre pour améliorer la sécurité de votre site web, vous devez désinfecter toutes les saisies des utilisateurs avant de les afficher, de les utiliser dans les requêtes SQL ou de les transmettre dans les appels du système ou du système de fichier.

- -
-

Attention : la leçon la plus importante à retenir concernant la sécurité d'un site web est de ne jamais faire confiance aux données du navigateur web. Cela comprend les requêtes GET avec la présence des paramètres dans l'URL, les données envoyées avec les POST, les en-têtes HTTP, les cookies, les fichiers chargés par l'utilisateur, etc. Il faut toujours vérifier et assainir les données. Il faut toujours s'attendre au pire.

-
- -

Quelques autres points que vous pouvez mettre en place :

- - - -

Les frameworks web peuvent aider à atténuer beaucoup des vulnérabilités les plus courantes.

- -

Résumé

- -

Cet article a présenté le concept de sécurité web et quelques-unes des menaces courantes vis-à-vis desquelles il faut se protéger. Le plus important à comprendre est qu'une application web ne peut pas faire confiance aux données provenant du navigateur ! Les données des utilisateurs doivent toutes être nettoyées avant d'être affichées ou utilisées dans les requêtes SQL ou dans les appels systèmes.

- -

C'est la fin de ce module, couvrant vos premiers pas dans le développement d'un site web côté serveur. Nous espérons que vous avez apprécié apprendre les concepts fondamentaux. Vous êtes désormais apte à choisir un framework web et à commencer à programmer.

- -

{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}

diff --git a/files/fr/learn/server-side/first_steps/website_security/index.md b/files/fr/learn/server-side/first_steps/website_security/index.md new file mode 100644 index 0000000000..4b90e6303a --- /dev/null +++ b/files/fr/learn/server-side/first_steps/website_security/index.md @@ -0,0 +1,163 @@ +--- +title: La sécurité d'un site Web +slug: Learn/Server-side/First_steps/Website_security +tags: + - Débutant + - Guide + - Sécurité + - Sécurité Web +translation_of: Learn/Server-side/First_steps/Website_security +original_slug: Learn/Server-side/Premiers_pas/Website_security +--- +
{{LearnSidebar}}
+ +
{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/Premiers_pas")}}
+ +

La sécurité d'un site web exige de la vigilance dans tous les aspects de sa conception et de son utilisation. Cet article d'introduction ne fera pas de vous un gourou de la sécurité des sites web, mais il vous aidera à comprendre d'où viennent les menaces et ce que vous pouvez faire pour renforcer votre application web contre les attaques les plus courantes.

+ + + + + + + + + + + + +
Pré-requis :Connaissances de base en informatique.
Objectif :Comprendre les menaces les plus courantes à la sécurité des applications web et ce que vous pouvez faire pour réduire le risque de piratage de votre site.
+ +

Qu'est-ce que la sécurité d'un site web?

+ +

Internet est un endroit dangereux ! Fréquemment, nous entendons parler de sites web devenus indisponibles en raison d'attaques par déni de service, ou affichant des informations modifiées (et souvent préjudiciables) sur leurs pages d'accueil. Dans d'autres cas, très médiatisés, des millions de mots de passe, d'adresses électroniques et de détails sur des cartes de crédit sont divulgués au public, exposant les utilisateurs du site web à la fois à l'embarras personnel et au risque de pertes financières.

+ +

L'objectif de la sécurité des sites web est de prévenir ces types d'attaques. Plus formellement, la sécurité des sites web est l'acte de protéger les sites web contre l'accès, l'utilisation, la modification, la destruction ou la perturbation non autorisées.

+ +

La sécurité efficace d'un site web nécessite un effort de conception sur l'ensemble du site : dans votre application web, dans la configuration du serveur web, dans vos politiques de création et de renouvellement des mots de passe et dans le code côté-client. Bien que tout cela semble très inquiétant, la bonne nouvelle est que si vous utilisez un framework web côté serveur, il incluera certainement par défaut des mécanismes de défense solides et bien pensés contre un certain nombre des attaques les plus courantes. D'autres attaques peuvent être atténuées grâce à la configuration de votre serveur web, par exemple en activant HTTPS. Enfin, les outils d'analyse de vulnérabilité accessibles au public peuvent vous aider à découvrir d'éventuelles erreurs dans votre conception.

+ +

Le reste de cet article détaille les menaces les plus courantes qui pèsent sur les sites web et quelques étapes simples pour protèger votre site.

+ +
+

Note : ceci est un article d'introduction, conçu pour vous aider à réflechir à la sécurité de votre site web. Il n'est en rien exhaustif.

+
+ +

Menaces visant la sécurité des sites web

+ +

Cette section n'énumère que quelques-unes des menaces les plus courantes visant les sites web et la façon dont elles sont tenues à distance. À mesure que vous lisez, notez comment les menaces apparaissent lorsque l'application web fait confiance ou n'est pas assez méfiante vis-à-vis des données provenant du navigateur !

+ +

Cross-Site Scripting (XSS)

+ +

XSS est un terme utilisé pour décrire une classe d'attaque qui permet à l'attaquant d'injecter des scripts, exécutés côté-client, au travers du site web pour viser le navigateur web des autres utilisateurs. Comme le code injecté provient du site web le navigateur web le considère comme sûr, il peut de ce fait faire des choses comme transmettre le cookie d'authentification de l'utilisateur à l'attaquant. Une fois que l'attaquant obtient ce cookie il peut se connecter sur le site comme si il était l'utilisateur attaqué et peut faire tout ce que l'utilisateur pourrait faire. En fonction du site sur lequel l'attaque se produit, cela peut inclure l'accès aux détails de carte bancaire, les informations des contacts, la modification du mot de passe, etc.

+ +
+

Note : les vulnérabilités XSS ont historiquement été les plus courantes.

+
+ +

Il y a deux manières principales pour demander au site de retourner un script injecté vers un navigateur web — elles sont désignées en tant que vulnérabilités XSS réfléchie et persistante.

+ + + +

La meilleur défense contre les vulnérabilités XSS est de supprimer ou désactiver toutes les balises qui peuvent potentiellement contenir des instructions pour exécuter du code. Pour HTML cela inclut les tags comme <script>, <object>, <embed>, et <link>.

+ +
+

Il est nécessaire de traiter les données saisies par l'utilisateur pour être sûr qu'il ne puisse ni exécuter de scripts ni pertuber le fonctionnement normal du site (ce procédé est appelé input sanitization en anglais). De nombreux frameworks proposent par défaut cette vérification sur les entrées des formulaires.

+
+ +

Injection SQL

+ +

L'injection SQL est une vulnérabilité qui permet à un attaquant d'exécuter du code SQL frauduleux sur une base de données, permettant l'accès, la modification ou la suppression des données quelque soit le droit de l'utilisateur. Une attaque par injection réussie peut permettre l'usurpation d'un compte, la création d'un compte avec les droits administrateur, l'accès à toute les données du serveur, ou la modification / destruction des données pour le rendre inutilisable.

+ +

Cette vulnérabilité est présente quand la saisie de l'utilisateur est transmise à une requête SQL sous-jacente qui peut modifier le sens de la requête. Par exemple, dans le code ci-dessous qui devrait lister l'ensemble des utilisateurs avec un nom particulier (userName) et qui provient d'un formulaire HTML:

+ +
statement = "SELECT * FROM users WHERE name = '" + userName + "';"
+ +

Si l'utilisateur entre un nom correct cela marchera comme voulu. Cependant un utilisateur malveillant peut changer complètement le sens de cette requête SQL pour obtenir la requête qui suit, simplement en ajoutant le texte, en gras ci dessous, en tant que nom, cette requête, modifiée, va créer une requête SQL valide qui va supprimer la table users et sélectionner toute les données de la table userinfo (révèlant les informations de chaque utilisateur). Tout cela est rendu possible à cause du début du texte injecté  ('a';) qui va complèter la requête d'origine (' est le symbole pour délimiter une chaine de texte en SQL).

+ +
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
+
+ +

Le moyen pour éviter ce type d'attaque est de s'assurer que toute saisie de l'utilisateur transmise à une requête SQL ne peut pas changer la nature de cette requête. Un moyen de faire cela est d'échapper tous les caractères de la saisie utilisateur quand ils ont un sens particulier en SQL.

+ +
+

Note : la requête SQL considère le symbole ' comme le début et la fin d'une chaine de texte. En ajoutant le caractère \ nous allons "échapper" ce symbole, et dire à SQL de le traiter comme une simple partie de la chaîne de caractères.

+
+ +

Dans la requête ci-dessous nous avons échappé le caractère '. Le SQL va donc interpréter la chaine complète (en gras) comme un nom (un nom étrange en effet, mais pas nuisible).

+ +
SELECT * FROM users WHERE name = 'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t';
+
+
+ +

Les frameworks web se chargent bien souvent d'échapper ces caractères à votre place. Django, par exemple, s'assure que toute saisie d'un utilisateur transmise au modèle est bien échappée.

+ +
+

Note : Cette section s'inspire beaucoup des informations de Wikipedia ici.

+
+ +

Falsification de requête inter-sites (CSRF)

+ +

Les attaques CSRF permettent à un utilisateur malveillant d'éxécuter des actions à l'a ide des identifiants d'un autre utilisateur sans que cet utilisateur ne soit informé ou consentant.

+ +

Ce type d'attaque s'explique mieux avec un exemple. John est l'utilisateur malveillant qui sait qu'un site particulier permet à des utilisateurs authentifiés d'envoyer de l'argent vers un compte particulier en utilisant des requêtes HTTP POST qui incluent le numéro de compte et le montant. John construit un formulaire qui inclut son numéro de compte et un montant dans des champs cachés (invisibles) et le transmet à un autre utilisateur du site (avec le bouton de validation déguisé en un lien vers un site "pour devenir riche".

+ +

Si un utilisateur clique sur le bouton de validation, une requête HTTP POST, contenant les informations de transaction, va être transmise au serveur ainsi que le cookie que le navigateur web associe au site (l'ajout à la requête du cookie associé au site est le comportement normal du navigateur). Le serveur va vérifier le cookie d'authentification, et l'utiliser pour déterminer si l'utilisateur est ou n'est pas connecté et donc permet ou non la transaction.

+ +

Au final tout utilisateur qui va cliquer sur le bouton de validation, alors qu'il sera connecté sur le site d'échange d'argent, va autoriser la transaction. John va devenir riche !

+ +
+

Note : l'astuce ici est que John n'a pas besoin d'accéder aux cookies de l'utilisateur (ou à ses identifiants), le navigateur web stocke cette information et l'inclut automatiquement dans toute les requêtes destinées au serveur associé.

+
+ +

Un moyen de prévenir ce type d'attaque est que le serveur demande que chaque requête POST possède un secret généré par le serveur et spécifique à l'utilisateur (le secret serait transmis par le serveur lors de l'envoi du formulaire de transaction). Cette approche empêche John de créer son propre formulaire car il n'est pas capable de connaitre le secret que le serveur founit à l'utilisateur. Même si il venait à trouver ce secret et créer un formulaire pour un utilisateur particulier, il ne pourrait pas utiliser ce formulaire pour attaquer d'autres utilisateurs

+ +

Les framework web incluent souvent des mécanismes afin de prévenir les attaques CSRF.

+ +

Autre menaces

+ +

Les autres attaques et vulnérabilités courantes incluent:

+ + + +

Il y en a beaucoup d'autres. Pour une liste plus exhaustive vous pouvez consulter la catégorie failles de sécurité Web (Wikipedia) et la catégorie Attaques (du projet OWASP).

+ +

Quelques messages clés

+ +

La majorité des attaques citées précédement réusissent lorsque l'application web fait confiance aux données provenant du navigateur web. Quoique vous fassiez d'autre pour améliorer la sécurité de votre site web, vous devez désinfecter toutes les saisies des utilisateurs avant de les afficher, de les utiliser dans les requêtes SQL ou de les transmettre dans les appels du système ou du système de fichier.

+ +
+

Attention : la leçon la plus importante à retenir concernant la sécurité d'un site web est de ne jamais faire confiance aux données du navigateur web. Cela comprend les requêtes GET avec la présence des paramètres dans l'URL, les données envoyées avec les POST, les en-têtes HTTP, les cookies, les fichiers chargés par l'utilisateur, etc. Il faut toujours vérifier et assainir les données. Il faut toujours s'attendre au pire.

+
+ +

Quelques autres points que vous pouvez mettre en place :

+ + + +

Les frameworks web peuvent aider à atténuer beaucoup des vulnérabilités les plus courantes.

+ +

Résumé

+ +

Cet article a présenté le concept de sécurité web et quelques-unes des menaces courantes vis-à-vis desquelles il faut se protéger. Le plus important à comprendre est qu'une application web ne peut pas faire confiance aux données provenant du navigateur ! Les données des utilisateurs doivent toutes être nettoyées avant d'être affichées ou utilisées dans les requêtes SQL ou dans les appels systèmes.

+ +

C'est la fin de ce module, couvrant vos premiers pas dans le développement d'un site web côté serveur. Nous espérons que vous avez apprécié apprendre les concepts fondamentaux. Vous êtes désormais apte à choisir un framework web et à commencer à programmer.

+ +

{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}

diff --git a/files/fr/learn/server-side/index.html b/files/fr/learn/server-side/index.html deleted file mode 100644 index 64bc78289b..0000000000 --- a/files/fr/learn/server-side/index.html +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Programmation de Sites Web côté serveur -slug: Learn/Server-side -tags: - - Beginner - - CodingScripting - - Intro - - Landing - - Learn - - Server - - Server-side programming - - Topic - - TopicStub -translation_of: Learn/Server-side ---- -
{{LearnSidebar}}
- -

Le sujet abordant les sites web dynamiques / la programmation coté serveur est une série de modules vous montrant comment créer un site web dynamique (sites web intereagissant avec vous de manière à vous donner des informations personnelles en réponse aux requêtes HTTP). Les modules fournissent une introduction universelle à la programmation coté serveur à travers des guides spécifiques pour les debutants vous montrant comment vous servir des infrastructure d'application (aussi appelé frameworks) Django (fait en Python) et Express(fait en Node.js/JavaScript) afin de créer des applications basiques.

- -

La plupart des sites web utilisent une technologie (frameworks, serveurs, languages, etc.) côté serveur afin d'afficher dynamiquement les différentes informations requises. Par exemple, imaginez combien de produits sont disponibles sur Amazon, et imaginez combien de post ont été publiés sur Facebook depuis son commencement ? Afficher tout ceci en utilisant uniquement des pages 'statiques' aurait été absolument inutile et complétement infernal, au lieu de ça les sites dynamiques utilisent des templates (sorte de page statique servant de 'modèle', programmé en utilisant le HTML, le CSS et le JavaScript dans lequel on viendra y insérer les informations voulues ; par exemple, chaque page d'article amazon est la même, il s'agit juste d'un template dans lequel viennent se placer les informations du produit sélectionné).

- -

De nos jours, dans le monde du web, apprendre la programmation côté serveur est indispensable.

- -

L'apprentissage

- -

Se lancer dans la programmation côté serveur est souvent plus facile que de se lancer dans la programmation côté client, parce que les sites dynamiques ont tendance à faire toujours la même chose (recevoir des données d'une base de données, et l'afficher dans une page, valider les entrées de l'utilisateur et les sauvegarder dans la base de données, vérifier les permissions utilisateur ainsi que les connections etc.), et sont en général construits autour d'un framework web rendant les opérations assez faciles.

- -

Des connaissances minimales en concepts de programmation pourront être utiles, mais pas essentielles. Les connaissances en développement côté client sont également utiles mais pas indispensables, considérez néamoins que cela vous aiderai à mieux travailler avec des développeurs qui créeront la partie côté client du site aussi appelé "front-end".

- -

Vous allez avoir besoin de comprendre "comment le fonctionne le Web". Nous vous recommandons premièremement de lire les sujets suivants :

- - - -

Ces articles vous donnerons les outils nécessaires afin de travailler avec les modules de cette section.

- -

Modules

- -

Ce sujet contient les modules suivants. Vous feriez mieux de commencer par le premier module, pour ensuite faire les autres qui vous montrerons comment travailler à l'aide des 2 languages de programmation côté serveur les plus populaire en utilisant les frameworks web appropriés.

- -
-
Les premiers pas en programmation de site web côté serveur
-
Ce module fournit les informations de base sur la programmation de site web côté serveur en répondant à des questions telles que "Qu'est-ce que c'est ?", "En quoi est-ce différent de la prgrammation côté client ?" ou encore "Pourquoi est-ce si utile ?" ainsi qu'un aperçu de quelques frameworks côté serveur et comment choisir le vôtre en fonction de vos besoin ou des besoins de votre site web. Finalement nous allons vous présenter les principes de sécurité en serveur web.
-
Le framework Web Django (Python)
-
Django est une infrastructure d'application (framework) côté serveur extrêmement populaire et dotée de son lot de fonctionnalités, écrite en Python. Ce module vous expliquera pourquoi Django fait un si bon framework pour serveurs, comment l'installer et s'en servir.
-
Express Web Framework (Node.js/JavaScript)
-
Express est une infrastructure d'application (framework) assez populaire, écrite en JavaScript et fonctionnant à l'aide de Node.js. Ce module explique les avantages dont bénéficie ce framework, comment l'installer ainsi que comment s'en servir.
-
- -

Voir aussi

- -
-
Serveur Node sans infrastucture d'application (framework)
-
Cet article comprend un serveur statique réalisé avec Node.js seul, pour ceux qui ne veulent pas utiliser de frameworks.
-
diff --git a/files/fr/learn/server-side/index.md b/files/fr/learn/server-side/index.md new file mode 100644 index 0000000000..64bc78289b --- /dev/null +++ b/files/fr/learn/server-side/index.md @@ -0,0 +1,58 @@ +--- +title: Programmation de Sites Web côté serveur +slug: Learn/Server-side +tags: + - Beginner + - CodingScripting + - Intro + - Landing + - Learn + - Server + - Server-side programming + - Topic + - TopicStub +translation_of: Learn/Server-side +--- +
{{LearnSidebar}}
+ +

Le sujet abordant les sites web dynamiques / la programmation coté serveur est une série de modules vous montrant comment créer un site web dynamique (sites web intereagissant avec vous de manière à vous donner des informations personnelles en réponse aux requêtes HTTP). Les modules fournissent une introduction universelle à la programmation coté serveur à travers des guides spécifiques pour les debutants vous montrant comment vous servir des infrastructure d'application (aussi appelé frameworks) Django (fait en Python) et Express(fait en Node.js/JavaScript) afin de créer des applications basiques.

+ +

La plupart des sites web utilisent une technologie (frameworks, serveurs, languages, etc.) côté serveur afin d'afficher dynamiquement les différentes informations requises. Par exemple, imaginez combien de produits sont disponibles sur Amazon, et imaginez combien de post ont été publiés sur Facebook depuis son commencement ? Afficher tout ceci en utilisant uniquement des pages 'statiques' aurait été absolument inutile et complétement infernal, au lieu de ça les sites dynamiques utilisent des templates (sorte de page statique servant de 'modèle', programmé en utilisant le HTML, le CSS et le JavaScript dans lequel on viendra y insérer les informations voulues ; par exemple, chaque page d'article amazon est la même, il s'agit juste d'un template dans lequel viennent se placer les informations du produit sélectionné).

+ +

De nos jours, dans le monde du web, apprendre la programmation côté serveur est indispensable.

+ +

L'apprentissage

+ +

Se lancer dans la programmation côté serveur est souvent plus facile que de se lancer dans la programmation côté client, parce que les sites dynamiques ont tendance à faire toujours la même chose (recevoir des données d'une base de données, et l'afficher dans une page, valider les entrées de l'utilisateur et les sauvegarder dans la base de données, vérifier les permissions utilisateur ainsi que les connections etc.), et sont en général construits autour d'un framework web rendant les opérations assez faciles.

+ +

Des connaissances minimales en concepts de programmation pourront être utiles, mais pas essentielles. Les connaissances en développement côté client sont également utiles mais pas indispensables, considérez néamoins que cela vous aiderai à mieux travailler avec des développeurs qui créeront la partie côté client du site aussi appelé "front-end".

+ +

Vous allez avoir besoin de comprendre "comment le fonctionne le Web". Nous vous recommandons premièremement de lire les sujets suivants :

+ + + +

Ces articles vous donnerons les outils nécessaires afin de travailler avec les modules de cette section.

+ +

Modules

+ +

Ce sujet contient les modules suivants. Vous feriez mieux de commencer par le premier module, pour ensuite faire les autres qui vous montrerons comment travailler à l'aide des 2 languages de programmation côté serveur les plus populaire en utilisant les frameworks web appropriés.

+ +
+
Les premiers pas en programmation de site web côté serveur
+
Ce module fournit les informations de base sur la programmation de site web côté serveur en répondant à des questions telles que "Qu'est-ce que c'est ?", "En quoi est-ce différent de la prgrammation côté client ?" ou encore "Pourquoi est-ce si utile ?" ainsi qu'un aperçu de quelques frameworks côté serveur et comment choisir le vôtre en fonction de vos besoin ou des besoins de votre site web. Finalement nous allons vous présenter les principes de sécurité en serveur web.
+
Le framework Web Django (Python)
+
Django est une infrastructure d'application (framework) côté serveur extrêmement populaire et dotée de son lot de fonctionnalités, écrite en Python. Ce module vous expliquera pourquoi Django fait un si bon framework pour serveurs, comment l'installer et s'en servir.
+
Express Web Framework (Node.js/JavaScript)
+
Express est une infrastructure d'application (framework) assez populaire, écrite en JavaScript et fonctionnant à l'aide de Node.js. Ce module explique les avantages dont bénéficie ce framework, comment l'installer ainsi que comment s'en servir.
+
+ +

Voir aussi

+ +
+
Serveur Node sans infrastucture d'application (framework)
+
Cet article comprend un serveur statique réalisé avec Node.js seul, pour ceux qui ne veulent pas utiliser de frameworks.
+
-- cgit v1.2.3-54-g00ecf