--- title: Django author: Steve Kossouho --- # Templates avec Django :::notes Préférer commencer par les vues, pour avoir des vues où utiliser des templates... ::: ---- ## Principe de templating Le concept de [templating]{.naming} (gabarits en français) consiste à utiliser des fichiers contenant du texte avec des emplacements réservés, qui seront remplis à la demande pour afficher des données personnalisées à l'utilisateur (par exemple, le système permet de générer un email de bienvenue commençant par « Bonjour `votre prénom` »). Ce concept est fourni par de nombreuses bibliothèques, et ce dans plusieurs langages (PHP, Javascript, C++, Python etc.) ---- ### Exemples de systèmes de templating en Python Il existe un certain nombre de moteurs de templating assez populaires en Python : - [Django](https://docs.djangoproject.com/en/3.2/topics/templates/) : Intégré à Django, propose un bon compromis entre performance et sécurité. - [Jinja 3](https://jinja.palletsprojects.com/en/3.0.x/) : Basé sur Django (et Genshi ?), mais utilisable dans tout projet Python. *Instagram* - [Mako](https://www.makotemplates.org/) : Performant, autorise le code Python brut. *Reddit* - [Genshi](https://genshi.readthedocs.io/en/latest/) : Utilisé dans TurboGears Django est à l'origine de deux moteurs de templates populaires, tels que `Jinja` pour Python et `Twig` pour PHP qui en reprennent largement la syntaxe. ---- ## Principe de templating dans Django Un moteur de templates propose une façon simple et rapide de générer du contenu en rédigeant des gabarits (des squelettes de document), des modèles où il ne reste plus qu'à renseigner des éléments manquants. Il existe plusieurs façons de procéder lorsqu'on conçoit un langage de templates : - Un moteur peut offrir la possibilité d'insérer des expressions Python sans limite dans un document de template - Un langage peut permettre d'écrire du Python brut pour générer du contenu - Un langage peut offrir des fonctionnalités limitées pour éviter des injections de code ou l'écriture de code métier en dehors du code principal ---- Les considérations suivantes ont mené à la création du moteur de templates de Django : - Les templates sont souvent rédigés et modifiés par des designers (HTML notamment) - Écrire du code Python ne devrait se faire que dans des fichiers Python - Exécuter du code Python en dehors de scripts Python normaux rend un programme plus dur à déboguer - Un designer ou intégrateur ne devrait pas être tenu d'apprendre le Python ---- Ainsi, le moteur de templates de Django repose sur des paradigmes simples : - Une syntaxe peu variée pour simplifier l'apprentissage (interpolation, attributs, filtres et balises) - Pas une ligne de Python autorisée (même si la syntaxe est similaire) - Système extensible, mais uniquement via du code Python ---- ## Le langage de templates de Django Toute la section qui va suivre concerne les éléments de langage spécifiques aux templates de Django. ---- ### Créer des templates Par défaut, Django ne sait trouver et exploiter des documents de templates que grâce à un paramètre `TEMPLATES` dans le fichier `settings` de votre projet : ```python {.numberLines} TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [], # Répertoires où trouver des templates "APP_DIRS": True, # Utiliser le répertoire `templates` de mes applications "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", ], }, }, ] ``` ---- - [Documentation sur le paramètre `TEMPLATES`](https://docs.djangoproject.com/en/dev/ref/settings/#templates) - La clé `BACKEND` permet de choisir le moteur de templates de Django (parmi deux) - La clé `APP_DIRS` permet d'indiquer si les répertoires `templates` des applications tierces sont automatiquement utilisés par le moteur. - La clé `DIRS`, elle, permet de spécifier des répertoires supplémentaires dans lesquels le moteur de templates de Django ira automatiquement chercher des fichiers de templates. ---- ### Nommage des templates Dans Django, lorsque vous souhaitez afficher un template au navigateur, vous avez accès à une fonction de raccourci nommée `django.shortcuts.render(request, template, context)`. Si vous appelez cette fonction avec les arguments `render(request, "index.html", {"value": 3.14159})`{.py} : - Django va considérer le premier fichier `index.html` trouvé dans les répertoires `templates` des applications tierces (celles indiquées dans `settings.INSTALLED_APPS`) - Le fichier `index.html` sera rendu en ayant accès aux données de `context` - La fonction renverra à la fin un objet `HttpResponse` avec le document rendu. - [Documentation sur la fonction `render`](https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render) ---- #### Exemple de template ```python {.numberLines} from django.shortcuts import render def index(request): return render(request, "index.html", {"value": 3.14159}) ``` Le template `index.html` peut être trouvé dans le dossier `templates` de votre application : ```html {.numberLines}
Value: {{ value }}
``` ---- ### Commentaires En HTML, la balise de commentaire s'écrit de la façon suivante : ```html {.numberLines} ``` C'est utile pour annoter un document, mais deux choses se produisent : - Le commentaire est visible dans le navigateur. - Écrire ce type de commentaire dans un document qui n'est pas du HTML ne fonctionnera pas. Avec Django, on peut insérer un vrai commentaire de template via la balise suivante : ```djangotemplate {.numberLines} {# Texte de commentaire Django #} ``` Le commentaire ne sera jamais rendu par le moteur, au même titre que les commentaires Python sont ignorés par l'interpréteur. Essayez autant que possible de vous en servir pour plus facilement relire vos templates _a posteriori_. ---- ### Interpolation de données Lorsque vous effectuez le rendu d'un template, le seul moyen d'y insérer des données est de les passer via un [contexte]{.naming}. Un contexte est un simple **dictionnaire Python**, où les clés sont des noms qui seront utilisables uniquement dans le template. Si en Python, on rend un template : ```python {.numberLines} from django.shortcuts import render def view_func(request): return render(request, "template.html", {"name": "Maurice"}) ``` Le template aura uniquement accès à une donnée nommée `name` et dont la valeur sera `"Maurice"`{.py}. (Voir [Documentation sur les modifieurs de contexte](https://docs.djangoproject.com/en/dev/ref/templates/api/#built-in-template-context-processors) pour savoir quels autres noms sont rendus disponibles dans le contexte de template) ```djangotemplate {.numberLines} Bonjour, {{ name }} ``` ---- #### Interpoler un attribut d'une variable Lorsqu'une des variables du contexte est un objet qui contient des attributs, vous pouvez les interpoler avec la syntaxe suivante : ```djangotemplate {.numberLines} {{ var1.attribute }} ``` ---- #### Interpoler un élément d'une variable (clé ou index) De la même façon, si votre variable de contexte est une **liste** ou un **dictionnaire** dont vous voulez récupérer un élement, et que vous connaissez son index ou bien sa clé, vous pouvez utiliser la syntaxe suivante : ```djangotemplate {.numberLines} {{ var1.nom_de_la_clé }} {# clé de dictionnaire qui est une chaîne #} {{ var2.numéro_d'index }} {# index de séquence #} ``` ---- #### Interpoler la valeur de retour d'une méthode Pour terminer, si votre variable de contexte est un objet dont vous voulez récupérer la valeur de retour d'une méthode, vous pouvez écrire : ```djangotemplate {{ var1.nom_de_la_methode }} ``` Cette méthode sera **toujours** appelée sans argument. ---- #### Interpolation et limitations Le langage de templates de Django a des restrictions sur les interpolations : - Vous ne pouvez pas passer d'arguments lorsque vous appelez une méthode et c'est intentionnel, seules les méthodes acceptant uniquement des arguments facultatifs sont utilisables. - Vous ne pouvez pas utiliser d'attributs ou méthodes commençant par `_`{.py} (considérées comme privées en Python). ---- ### Filtres de templates Lorsque vous effectuez une interpolation dans un template (`{{ ... }}`), vous avez la possibilité de transformer l'interpolation, souvent pour des questions de présentation. Pour effectuer ce genre de traitement sur une interpolation, vous devez utiliser des [filtres]{.naming}. Les filtres permettent de transformer une interpolation et fonctionnent sur chaînes, dates, listes et d'autres types. (voir [filtres intégrés à Django](https://docs.djangoproject.com/en/dev/ref/templates/builtins/#built-in-filter-reference)) ---- #### Filtre basique (sans argument) Un filtre s'utilise simplement en le "pipant" à l'expression à transformer. Le résultat affiché sera celui du filtre. Il est possible d'utiliser plusieurs filtres successivement en les chaînant. ```djangotemplate {.numberLines} {# Utilisation du filtre upper, pour mettre en majuscules #}Date de création : {{ moment|date:"d m Y" }}
``` ---- #### Filtres fréquents Parmi les nombreux [filtres disponibles de base dans Django](https://docs.djangoproject.com/en/dev/ref/templates/builtins/#built-in-filter-reference), certains seront plus souvent utilisés que d'autres, comme par exemple : | Filtre | Description | |--------------------------------------------|----------------------------------------------------------------------------------------------------| | `upper` et `lower` | Pour changer la casse du texte (majuscules et minuscules). | | `default:value` | Si la valeur équivaut à `False`, utiliser `value` par défaut. | | `default_if_none:value` | Si la valeur équivaut à `None`, utiliser `value` par défaut. | | `date:format` | Interpoler un objet `datetime` avec un format spécifique. | | `length` | Interpoler la longueur d'une collection. | | `safe` | Ne pas échapper le contenu à interpoler. Permet d'insérer du HTML. | | `truncatechars:num` et `truncatewords:num` | Couper et placer une ellipse (tronquer le texte après un certain nombre de caractères ou de mots). | ---- ### Balises de templates Nous venons de voir que l'on pouvait "filtrer" du contenu provenant d'une interpolation. Et s'il était possible d'utiliser des outils plus avancés, permettant de générer des données dynamiques, ou même de rendre du contenu conditionnel, par exemple ? Le moteur de templates de Django fournit un système de [balises]{.naming} qui nous permet d'accéder à des comportements plus avancés. Grâce aux balises, nous allons pouvoir utiliser des boucles, conditionner le rendu d'un segment de template, configurer le rendu par le moteur ou encore modifier le contexte de rendu... ---- ### Syntaxe des balises Pour utiliser des balises de gabarit, le plus important, c'est d'en connaitre la syntaxe. Pour utiliser une balise, il faut écrire : ```djangotemplate {.numberLines} {% tag_name argument1 argument2... %} ``` Entre les marqueurs `{% ... %}` il suffit d'indiquer le nom de la balise et d'y ajouter, séparés par des espaces, autant d'arguments que ce qu'accepte la balise. Certaines balises fonctionnent par paire, comme `{% if %}…{% endif %}`{.djangotemplate}, ou `{% for %}…{% endfor %}`{.djangotemplate}. Ces balises sont documentées dans la [documentation officielle de Django](https://docs.djangoproject.com/en/dev/ref/templates/builtins/#built-in-tag-reference) ---- ### Balises natives Pour aborder le chapitre des balises, nous allons nous intéresser à certaines balises précises dédiées à des outils importants. ---- #### Conditions | Balise | Description | |---------------|-------------------------------------------------------------------------------------------| | `{% if %}` | Évalue une condition et affiche le contenu si la condition est vraie. | | `{% elif %}` | Alternative à `{% if %}` si la première condition est fausse. | | `{% else %}` | Définit un bloc à afficher si aucune des conditions précédentes n'est vraie. | | `{% endif %}` | Termine une instruction conditionnelle. | ---- #### Boucles | Balise | Description | |------------------------|-------------------------------------------------------------------------------------------| | `{% for ... in ... %}` | Itère sur une séquence d'objets. | | `{% empty %}` | Définit un bloc à afficher si la séquence est vide. | | `{% endfor %}` | Termine une boucle. | ---- #### Autres Balises Importantes | Balise | Description | |---------------------------------------|----------------------------------------------------------------| | `{% include %}` | Inclut un autre template. | | `{% extends %}` | Indique que le template hérite d'un autre template. | | `{% block %}` | Définit un bloc de contenu modifiable dans un template hérité. | | `{% endblock %}` | Termine un bloc de contenu. | | `{% url %}` | Génère une URL vers une vue nommée. | | `{% csrf_token %}` | Génère un jeton CSRF pour sécuriser les formulaires. | | `{% comment %}` et `{% endcomment %}` | Commente une section du template. | | `{% load %}` | Charge une bibliothèque de tags personnalisés. | | `{% with %}` et `{% endwith %}` | Assigne une ou plusieurs variables pour une portée limitée. | | `{% endwith %}` | Termine le bloc `with`. | ---- | Balise | Description | |-----------------------|--------------------------------------------------------------------------------------| | `{% autoescape %}` | Active ou désactive l'échappement automatique dans un bloc. | | `{% endautoescape %}` | Termine le bloc `autoescape`. | | `{% now %}` | Affiche la date et l'heure actuelles selon un format spécifié. | | `{% spaceless %}` | Supprime les espaces blancs entre les balises HTML dans le contenu du bloc. | | `{% endspaceless %}` | Termine le bloc `spaceless`. | | `{% verbatim %}` | Ignore le rendu des balises Django dans le contenu du bloc. | | `{% endverbatim %}` | Termine le bloc `verbatim`. | ---- ### Structures de contrôle : `if` Vous pouvez grace aux balises `{% if %}`{.djangotemplate} et `{% else %}`{.djangotemplate} construire un document dont certaines zones seront rendues conditionnellement. La syntaxe de la balise est similitaire aux structures conditionnelles en Python. ```djangotemplate {.numberLines} {% if expression %} Contenu si la condition est vraie {% elif expression %} Contenu si la premiere condition est fausse {% else %} Contenu si la condition est fausse {% endif %} ``` ---- ### Structures de contrôle : `for` Si vous souhaitez générer du contenu pour chaque élément d'un itérable, vous pouvez utiliser la balise `{% for %}`{.djangotemplate}. La syntaxe de la balise est similaire aux structures de boucle en Python, à l'exception de l'existence d'une clause `{% empty %}`{.djangotemplate}, dont le contenu sera affiché si l'itérable est vide. ```djangotemplate {.numberLines} {% for name in iterable %} Contenu de l'itération {{ name }} {% empty %} Contenu si la boucle est vide {% endfor %} ``` ---- ### Inclusion de templates La balise `{% include %}`{.djangotemplate} permet d'inclure un autre template dans le template actuel. L'intérêt d'inclure des templates est de pouvoir les utiliser comme **composants** dans certains cas répétitifs. Il est possible de transmettre un contexte spécifique lors de l'inclusion de templates. ```djangotemplate {.numberLines} {% for user in users %} {% include "path/to/user_template.html" with user=user %} {% endfor %} ``` ```djangotemplate {.numberLines}{{ user.username }}