Aller au contenu
Développement medium

Utilisation des Templates Jinja2

35 min de lecture

logo jinja

Jinja2 est un puissant moteur de templates pour python, utilisé principalement pour séparer la logique métier de la présentation dans les applications web. Il permet de créer des fichiers HTML ou tout autre type de fichier texte contenant des balises spéciales, qui sont ensuite remplacées par des données dynamiques fournies par un programme Python. L'idée est de maintenir une structure claire et réutilisable, en générant des interfaces dynamiques basées sur des données variables.

Jinja2 est particulièrement populaire grâce à sa flexibilité et sa simplicité. Il est intégré dans des frameworks comme Flask, qui s'appuie sur Jinja2 pour gérer la génération de pages HTML, mais aussi dans des outils d'automatisation comme Ansible, où il permet de configurer des fichiers à la volée en fonction de variables spécifiques.

Un des grands avantages de Jinja2 réside dans son approche permettant de mélanger du code python directement dans les templates tout en garantissant une séparation claire entre le traitement des données et leur affichage. Grâce à cette approche, les développeurs peuvent concentrer leur logique métier dans les fichiers Python, tout en confiant la gestion de l'affichage des données aux templates.

Ce guide vise à vous donner une compréhension complète de Jinja2, depuis son installation jusqu'à des cas d'utilisation avancés. Que vous souhaitiez générer des pages web dynamiques ou automatiser des configurations, Jinja2 est un outil essentiel pour améliorer la flexibilité et la maintenabilité de vos projets.

  • Écrire un template avec variables ({{ }}) et logique ({% %}).
  • Utiliser boucles, conditions et filtres.
  • Factoriser avec l'héritage, les includes et les macros.
  • Charger des templates depuis des fichiers.
  • Sécuriser le rendu (auto-échappement, SSTI).

Avant de commencer à utiliser Jinja2, il est essentiel de l'installer correctement dans votre environnement de développement. Comme Jinja2 est une bibliothèque Python, elle peut être facilement installée via pip, le gestionnaire de paquets Python.

Il est préférable d'utiliser des environnements virtuels pour isoler les dépendances d'un projet. Cela permet d'éviter les conflits entre les différentes versions des bibliothèques Python installées sur votre système. Pour cela, vous pouvez utiliser venv, qui est inclus dans Python à partir de la version 3.3.

Pour créer un environnement virtuel, naviguez dans le répertoire de votre projet et exécutez la commande suivante :

Fenêtre de terminal
python3 -m venv env

Cela crée un répertoire nommé env contenant une copie isolée de Python. Ensuite, vous devez activer l'environnement virtuel :

Fenêtre de terminal
source env/bin/activate
  • Sous Windows :
Fenêtre de terminal
env\Scripts\activate

Une fois l'environnement activé, vous pouvez installer Jinja2 sans affecter d'autres projets Python sur votre machine :

La méthode la plus courante pour installer Jinja2 est d'utiliser pip, qui est généralement fourni avec Python. Pour vérifier que pip est installé et à jour, vous pouvez exécuter la commande suivante dans votre terminal :

Fenêtre de terminal
pip --version

Si pip est correctement installé, vous devriez voir la version actuelle. Une fois vérifié, vous pouvez installer Jinja2 avec la commande suivante :

Fenêtre de terminal
pip install Jinja2

Cette commande télécharge et installe la dernière version stable de Jinja2 depuis le dépôt PyPI. Si vous travaillez sur un projet spécifique et souhaitez verrouiller la version utilisée, il est recommandé d'installer une version spécifique de Jinja2 en précisant son numéro :

Fenêtre de terminal
pip install Jinja2==3.1.6

Cela garantit que la version de Jinja2 utilisée dans votre projet ne changera pas, même si une nouvelle version est publiée.

Pour vérifier que Jinja2 est correctement installé et disponible dans votre projet, vous pouvez lancer une session Python interactive et essayer d'importer Jinja2 :

Fenêtre de terminal
python
>>> import jinja2
>>> print(jinja2.__version__)

Si aucune erreur ne s'affiche et que la version de Jinja2 est imprimée, cela signifie que l'installation s'est bien déroulée. Vous êtes maintenant prêt à commencer à utiliser Jinja2 dans vos projets.

Installation via d'autres outils de gestion de projets

Section intitulée « Installation via d'autres outils de gestion de projets »

Pour ceux qui utilisent des gestionnaires de projets comme Poetry ou Pipenv, l'installation de Jinja2 est tout aussi simple. Avec Poetry, vous pouvez ajouter Jinja2 en utilisant la commande suivante dans le répertoire de votre projet :

Fenêtre de terminal
poetry add Jinja2

De même, avec Pipenv :

Fenêtre de terminal
pipenv install Jinja2

Ces outils permettent également une gestion avancée des dépendances et des versions, tout en facilitant le travail en équipe sur des environnements partagés.

Enfin, si vous avez besoin de mettre à jour Jinja2 vers une version plus récente, il suffit d'exécuter la commande suivante :

Fenêtre de terminal
pip install --upgrade Jinja2

Cela vous permettra de bénéficier des dernières fonctionnalités et corrections de bugs, tout en s'assurant que votre projet reste à jour avec les dernières bonnes pratiques.

Avec Jinja2 correctement installé et configuré, vous pouvez désormais explorer les bases de la création de templates et de la génération dynamique de contenu.

Une fois Jinja2 installé, il est temps de se plonger dans sa syntaxe de base. L'un des principaux avantages de Jinja2 est sa simplicité d'utilisation pour intégrer des données dynamiques dans des fichiers statiques, comme du HTML. Que ce soit pour afficher des variables, appliquer des filtres, ou ajouter des conditions, la syntaxe de Jinja2 reste claire et intuitive.

Il est souvent utile de commenter des parties de vos templates, que ce soit pour documenter le code ou désactiver temporairement une section. Jinja2 permet d'ajouter des commentaires dans les templates en utilisant la syntaxe suivante :

{# Ceci est un commentaire #}

Ces commentaires ne seront pas visibles dans le rendu HTML final.

Le principe de base de Jinja2 consiste à insérer des variables dans les templates. En Python, ces variables peuvent être des chaînes, des nombres, des listes ou même des objets complexes. Pour les afficher dans un template Jinja2, il suffit d'utiliser la syntaxe suivante :

<h1>{{ titre }}</h1>

Ici, la variable titre, qui est passée depuis le code Python, sera affichée dans le HTML. Si, par exemple, titre est défini dans le code Python comme suit :

titre = "Bienvenue sur mon site"

Le template Jinja2 génèrera le HTML suivant :

<h1>Bienvenue sur mon site</h1>

Cette approche permet d'injecter dynamiquement des données dans le contenu HTML. Vous pouvez également accéder aux attributs des objets, comme ceci :

<p>Nom : {{ utilisateur.nom }}</p>

Si l'objet utilisateur contient un attribut nom, il sera affiché dans le paragraphe HTML.

Dans certains cas, une variable que vous essayez d'afficher dans un template peut ne pas être définie. Par défaut, Jinja2 ne lève aucune erreur pour une variable inexistante : il affiche une chaîne vide à la place. Ce comportement silencieux est pratique, mais il masque les oublis.

Si une variable manquante doit au contraire interrompre le rendu, changez la politique des variables non définies au niveau de l'environnement avec StrictUndefined. Toute variable absente lève alors une UndefinedError :

from jinja2 import Environment, StrictUndefined
env = Environment(undefined=StrictUndefined)
env.from_string("{{ variable }}").render() # lève UndefinedError

Il n'existe pas de filtre required en Jinja2 natif. Pour simplement tester la présence d'une variable sans lever d'erreur, utilisez le test is defined présenté dans les conditions. Ansible, lui, fournit son propre filtre mandatory qui joue ce rôle dans ses playbooks.

Outre les variables, Jinja2 permet d'écrire des expressions complexes directement dans les templates. Par exemple, vous pouvez effectuer des calculs simples ou manipuler des données à la volée :

<p>Le prix avec remise est de : {{ prix * 0.9 }}</p>

Il est aussi possible d'accéder aux éléments de listes ou de dictionnaires, comme ceci :

<p>Premier élément : {{ liste[0] }}</p>

Jinja2 offre des filtres pour transformer ou formater les données avant de les afficher. Les filtres sont appliqués en ajoutant un pipe (|) après une variable, suivi du nom du filtre. Voici quelques exemples de filtres couramment utilisés :

  • upper : Convertit une chaîne de caractères en majuscules.
<p>{{ titre|upper }}</p>

Si titre = "Bonjour", le template générera :

<p>BONJOUR</p>
  • default : Définit une valeur de repli si la variable n'est pas définie. Attention, ce filtre ne se déclenche pas sur une chaîne vide ni sur une valeur « fausse » (0, False) : il faut lui passer un second argument true pour couvrir aussi ces cas.
<p>{{ utilisateur.nom|default("Invité") }}</p>

Si utilisateur.nom n'est pas défini, Jinja2 affichera "Invité". En revanche, si la variable existe mais vaut une chaîne vide, le filtre la laisse telle quelle. Pour couvrir aussi ce cas, activez le second paramètre :

<p>{{ utilisateur.nom|default("Invité", true) }}</p>
  • length : Calcule la longueur d'une liste ou d'une chaîne.
<p>Nombre d'articles : {{ articles|length }}</p>

Jinja2 propose une large gamme de filtres intégrés. Vous pouvez aussi combiner plusieurs filtres pour manipuler les données de manière plus complexe :

<p>{{ message|trim|lower }}</p>

Ici, le filtre trim supprime les espaces avant et après la chaîne, et lower la convertit en minuscules.

L'une des forces de Jinja2 est sa capacité à intégrer des boucles et des conditions directement dans les templates, permettant de générer du contenu dynamique basé sur des collections de données ou des décisions logiques.

Tout comme en Python, les conditions dans Jinja2 permettent de contrôler ce qui est affiché en fonction de l'état des variables. La syntaxe est similaire à Python, avec des balises Jinja2 spécifiques. Voici un exemple de condition basique dans un template :

{% if utilisateur.est_admin %}
<p>Bienvenue, administrateur {{ utilisateur.nom }}.</p>
{% else %}
<p>Bienvenue, {{ utilisateur.nom }}.</p>
{% endif %}

Ici, si l'attribut est_admin de l'objet utilisateur est vrai, le template affichera un message de bienvenue spécifique à l'administrateur. Sinon, un message générique sera affiché.

Jinja2 supporte également des conditions multiples avec les mots-clés elif et else, permettant de gérer plusieurs cas en une seule expression. Par exemple, pour afficher différents messages selon le rôle de l'utilisateur :

{% if utilisateur.role == "admin" %}
<p>Bienvenue, administrateur {{ utilisateur.nom }}.</p>
{% elif utilisateur.role == "modérateur" %}
<p>Bienvenue, modérateur {{ utilisateur.nom }}.</p>
{% else %}
<p>Bienvenue, utilisateur {{ utilisateur.nom }}.</p>
{% endif %}

Ce code permet d'afficher des messages personnalisés selon le rôle de l'utilisateur, avec un message par défaut pour les utilisateurs sans rôle particulier.

Il est également possible d'utiliser des opérateurs logiques comme and, or et not pour créer des conditions plus complexes. Par exemple :

{% if utilisateur.est_admin and utilisateur.actif %}
<p>L'administrateur {{ utilisateur.nom }} est actif.</p>
{% elif not utilisateur.actif %}
<p>L'utilisateur {{ utilisateur.nom }} est inactif.</p>
{% endif %}

Dans cet exemple, la condition vérifie si l'utilisateur est un administrateur actif. Si ce n'est pas le cas, une autre condition vérifie s'il est inactif.

Jinja2 offre des tests prédéfinis qui permettent de simplifier certaines vérifications courantes. Par exemple, pour vérifier si une variable est définie, vous pouvez utiliser le test is defined :

{% if utilisateur.nom is defined %}
<p>Nom de l'utilisateur : {{ utilisateur.nom }}</p>
{% else %}
<p>Nom non défini.</p>
{% endif %}

Voici quelques tests utiles fournis par Jinja2 :

  • is defined : Vérifie si une variable est définie.
  • is none : Vérifie si une variable est None.
  • is odd : Vérifie si un nombre est impair.
  • is divisibleby(3) : Vérifie si un nombre est divisible par 3.

Ces tests peuvent être combinés avec des conditions pour rendre le template plus robuste.

Les boucles sont essentielles lorsqu'il s'agit de répéter un bloc de code pour chaque élément d'une liste, d'un dictionnaire ou d'une autre structure de données itérable. Dans Jinja2, la syntaxe de la boucle est similaire à celle de Python, mais encapsulée dans des balises spécifiques.

Voici un exemple simple de boucle for qui affiche les éléments d'une liste d'articles dans une balise HTML <ul> :

<ul>
{% for article in articles %}
<li>{{ article.titre }}</li>
{% endfor %}
</ul>

Dans cet exemple, si articles est une liste d'objets avec des attributs titre, Jinja2 générera une balise <li> pour chaque article. Par exemple, si le code Python est :

articles = [
{"titre": "Article 1"},
{"titre": "Article 2"},
{"titre": "Article 3"}
]

Le template Jinja2 générera le HTML suivant :

<ul>
<li>Article 1</li>
<li>Article 2</li>
<li>Article 3</li>
</ul>

Jinja2 offre également des variables spéciales accessibles dans les boucles pour obtenir des informations supplémentaires sur l'itération en cours. Par exemple, la variable loop.index renvoie l'index actuel (commençant à 1) dans la boucle. Utiliser ces variables permet d'ajouter plus de contrôle et de personnalisation au rendu des boucles.

<ul>
{% for article in articles %}
<li>{{ loop.index }} - {{ article.titre }}</li>
{% endfor %}
</ul>

Cela produira le résultat suivant :

<ul>
<li>1 - Article 1</li>
<li>2 - Article 2</li>
<li>3 - Article 3</li>
</ul>

Voici quelques variables utiles dans une boucle :

  • loop.index : l'index de l'élément actuel (commence à 1)
  • loop.first : vaut True si l'élément est le premier
  • loop.last : vaut True si l'élément est le dernier

Ces variables peuvent être très utiles pour personnaliser la présentation des éléments en fonction de leur position dans la boucle.

Jinja2 permet aussi d'ajouter une clause else dans les boucles. Cela est particulièrement utile pour gérer les cas où la liste ou l'itérable est vide. Si l'itérable ne contient aucun élément, le bloc else sera exécuté.

<ul>
{% for article in articles %}
<li>{{ article.titre }}</li>
{% else %}
<li>Aucun article disponible.</li>
{% endfor %}
</ul>

Si la liste articles est vide, l'élément de liste "Aucun article disponible." sera affiché à la place.

Gestion des erreurs dans les boucles et conditions

Section intitulée « Gestion des erreurs dans les boucles et conditions »

Il peut arriver qu'une variable ne soit pas définie ou qu'elle génère une erreur pendant le rendu. Jinja2 permet de gérer cela de manière élégante avec des filtres comme default ou des tests comme is defined, pour éviter les comportements inattendus. Par exemple :

<p>{{ utilisateur.email|default("Email non disponible") }}</p>

Ici, si l'email de l'utilisateur n'est pas défini, le texte "Email non disponible" sera affiché à la place.

Les macros dans Jinja2 sont un moyen puissant et efficace de réutiliser du code dans les templates, de manière similaire à une fonction dans le langage Python. Elles permettent de centraliser du code commun à plusieurs parties de votre application, de réduire les répétitions et de rendre les templates plus propres et plus faciles à maintenir.

Les macros fonctionnent comme des fonctions dans les langages de programmation traditionnels : elles acceptent des paramètres, exécutent des instructions, puis retournent un résultat. Dans un template Jinja2, une macro est définie à l'aide de la balise {% macro %}. Voici un exemple de macro simple qui affiche une balise HTML <input> pour un formulaire :

{% macro input_field(nom, valeur='', type='text') %}
<input type="{{ type }}" name="{{ nom }}" value="{{ valeur }}">
{% endmacro %}

Cette macro, appelée input_field, prend trois paramètres : nom, valeur (qui a une valeur par défaut) et type (par défaut, "text"). Vous pouvez maintenant utiliser cette macro plusieurs fois dans votre template pour générer des champs de formulaire, sans répéter le code HTML :

<form>
{{ input_field('username') }}
{{ input_field('password', type='password') }}
{{ input_field('email', type='email') }}
</form>

Le résultat généré sera le suivant :

<form>
<input type="text" name="username" value="">
<input type="password" name="password" value="">
<input type="email" name="email" value="">
</form>

Comme avec les fonctions Python, les macros peuvent accepter des arguments pour adapter leur comportement. Vous pouvez définir des valeurs par défaut pour les paramètres des macros, ce qui permet de personnaliser l'affichage tout en réduisant les répétitions. Prenons un exemple plus avancé où nous ajoutons des classes CSS aux champs de formulaire générés par la macro :

{% macro input_field(nom, valeur='', type='text', classe='form-control') %}
<input type="{{ type }}" name="{{ nom }}" value="{{ valeur }}" class="{{ classe }}">
{% endmacro %}

Maintenant, vous pouvez personnaliser l'apparence des champs en passant une classe CSS différente à la macro :

<form>
{{ input_field('username', classe='form-input') }}
{{ input_field('password', type='password', classe='form-password') }}
</form>

Cela génèrera le code suivant avec des classes CSS spécifiques :

<form>
<input type="text" name="username" value="" class="form-input">
<input type="password" name="password" value="" class="form-password">
</form>

Réutilisation de macros dans différents templates

Section intitulée « Réutilisation de macros dans différents templates »

Un avantage majeur des macros est qu'elles peuvent être définies une fois dans un template séparé et réutilisées dans plusieurs autres templates grâce aux balises {% import %} ou {% from %}. Cela permet de centraliser le code commun dans un fichier séparé et de l'inclure dans d'autres templates.

Par exemple, supposons que vous définissiez une macro dans un fichier appelé macros.html :

macros.html
{% macro button(label, style='primary') %}
<button class="btn btn-{{ style }}">{{ label }}</button>
{% endmacro %}

Ensuite, vous pouvez importer cette macro dans un autre template pour l'utiliser :

page.html
{% from "macros.html" import button %}
<p>{{ button('Envoyer') }}</p>
<p>{{ button('Annuler', 'secondary') }}</p>

Le code généré sera :

<p><button class="btn btn-primary">Envoyer</button></p>
<p><button class="btn btn-secondary">Annuler</button></p>

Cette technique permet de garder les templates légers et modifiables en un seul endroit, réduisant les risques d'erreurs liées à des duplications de code.

Il est possible de passer non seulement des valeurs primitives (comme des chaînes ou des nombres) aux macros, mais également un bloc de contenu entier. Cela permet de créer des composants réutilisables qui enveloppent du contenu personnalisé. Jinja2 s'appuie pour cela sur la balise {% call %} et la fonction spéciale caller(), qui rend le bloc passé à l'appel. Voici une macro « carte » qui entoure n'importe quel contenu :

{% macro carte(titre) %}
<div class="carte">
<h3>{{ titre }}</h3>
<div class="contenu">
{{ caller() }}
</div>
</div>
{% endmacro %}

Pour l'utiliser, vous appelez la macro avec {% call %} et placez le contenu entre la balise d'ouverture et {% endcall %} :

{% call carte('Mon Titre') %}
<p>Voici le contenu de la carte.</p>
{% endcall %}

Ce code génère :

<div class="carte">
<h3>Mon Titre</h3>
<div class="contenu">
<p>Voici le contenu de la carte.</p>
</div>
</div>

Le mécanisme {% call %} / caller() est la façon idiomatique de Jinja2 pour passer un bloc à une macro. Les fonctions anonymes de Python (lambda) ne sont pas interprétées dans un template : les écrire directement provoque une erreur de syntaxe.

Jinja2 propose également des options pour contrôler la gestion des espaces blancs dans les templates. Par défaut, les espaces blancs (retours à la ligne, espaces supplémentaires) sont préservés, mais vous pouvez les supprimer en utilisant des délimiteurs spéciaux. Par exemple :

{% if utilisateur.nom -%}
<p>{{ utilisateur.nom }}</p>
{%- endif %}

Ici, le délimiteur -%} supprime les espaces blancs autour du bloc if.

L’héritage de templates est l'une des fonctionnalités les plus puissantes de Jinja2, permettant de créer des structures de templates modulaires et réutilisables. L’idée principale derrière l’héritage de templates est de définir un template de base qui sert de squelette, puis de permettre aux autres templates d’étendre cette structure en redéfinissant ou en ajoutant des sections spécifiques. Cela permet de centraliser la mise en page commune tout en permettant la personnalisation dans chaque template enfant.

L’héritage des templates fonctionne grâce à la balise {% extends %}, qui permet à un template d'hériter d'un autre template et à la balise {% block %}, qui définit des sections de contenu modifiable. Un template de base contient la mise en page générale, tandis que les templates enfants redéfinissent uniquement les blocs nécessaires sans avoir à répéter tout le code HTML.

Prenons un exemple classique avec un template de base appelé base.html :

base.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Mon Site{% endblock %}</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<header>
<h1>{% block header %}Bienvenue sur mon site{% endblock %}</h1>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>&copy; 2024 Mon Site</p>
</footer>
</body>
</html>

Ce template base.html définit la structure globale de la page avec des sections pour le titre, l'en-tête et le contenu principal à l'aide des balises {% block %}. Ces blocs peuvent ensuite être redéfinis dans les templates enfants.

Pour créer une page spécifique, par exemple une page d'accueil, nous allons étendre le template de base et remplir les sections définies dans les blocs. Voici un exemple de template enfant index.html :

index.html
{% extends "base.html" %}
{% block title %}Accueil{% endblock %}
{% block header %}
<h1>Bienvenue sur la page d'accueil de mon site</h1>
{% endblock %}
{% block content %}
<p>Ceci est la page d'accueil. Nous proposons différents services.</p>
{% endblock %}

Ce template enfant commence par la directive {% extends %} qui indique qu’il hérite du template base.html. Ensuite, il remplit les blocs title, header et content avec un contenu personnalisé. Jinja2 remplace alors les sections correspondantes du template de base par le contenu fourni dans le template enfant.

Le rendu final sera :

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accueil</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<header>
<h1>Bienvenue sur la page d'accueil de mon site</h1>
</header>
<main>
<p>Ceci est la page d'accueil. Nous proposons différents services.</p>
</main>
<footer>
<p>&copy; 2024 Mon Site</p>
</footer>
</body>
</html>

L'un des avantages de l'utilisation de {% block %} est que vous pouvez définir un contenu par défaut dans le template de base. Si un bloc n'est pas redéfini dans un template enfant, Jinja2 utilisera le contenu par défaut. Par exemple, dans le template base.html ci-dessus, le bloc title a déjà un contenu par défaut (Mon Site). Si un template enfant ne redéfinit pas ce bloc, le titre "Mon Site" sera utilisé.

Prenons un exemple avec un autre template enfant about.html, qui ne redéfinit pas tous les blocs :

about.html
{% extends "base.html" %}
{% block content %}
<p>Bienvenue sur la page À propos. Ici, vous en apprendrez plus sur nous.</p>
{% endblock %}

Le bloc title et le bloc header ne sont pas redéfinis dans ce template enfant. Jinja2 utilisera donc les valeurs par défaut définies dans base.html. Le rendu final sera :

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mon Site</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<header>
<h1>Bienvenue sur mon site</h1>
</header>
<main>
<p>Bienvenue sur la page À propos. Ici, vous en apprendrez plus sur nous.</p>
</main>
<footer>
<p>&copy; 2024 Mon Site</p>
</footer>
</body>
</html>

En plus de l'héritage de templates, Jinja2 propose la balise {% include %}, qui permet d'inclure d'autres templates dans une page. C'est très utile pour insérer des composants communs, tels que des en-têtes, des barres de navigation ou des sections de pied de page.

Voici un exemple où nous incluons un menu de navigation dans plusieurs pages :

menu.html
<nav>
<ul>
<li><a href="/">Accueil</a></li>
<li><a href="/about">À propos</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>

Nous pouvons inclure ce menu dans le template base.html :

base.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Mon Site{% endblock %}</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<header>
<h1>{% block header %}Bienvenue sur mon site{% endblock %}</h1>
{% include "menu.html" %}
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>&copy; 2024 Mon Site</p>
</footer>
</body>
</html>

Le fichier menu.html sera inclus chaque fois que le template base.html est utilisé, ce qui garantit que toutes les pages de votre site contiennent la même barre de navigation sans la dupliquer dans chaque fichier.

Lors de l'utilisation de Jinja2, comme dans tout langage ou framework, il est possible de rencontrer des erreurs qui affectent le rendu des templates. Contrairement à Python, Jinja2 ne dispose pas de structure try/except dans les templates : la gestion des erreurs repose sur trois mécanismes complémentaires, à choisir selon que vous voulez ignorer, remplacer ou signaler une donnée manquante.

La façon la plus lisible d'éviter une erreur est de vérifier la présence de la donnée avant de l'utiliser, avec le test is defined :

{% if utilisateur.age is defined %}
<p>Âge : {{ utilisateur.age }}</p>
{% else %}
<p>Âge non disponible</p>
{% endif %}

Le bloc else joue ici le rôle du except : il fournit un contenu de repli quand la variable manque, sans jamais interrompre le rendu.

Pour une simple valeur manquante, le filtre default est plus concis qu'une condition complète :

<p>Âge : {{ utilisateur.age|default("non disponible") }}</p>

Le comportement global face aux variables manquantes se règle au niveau de l'environnement, via le paramètre undefined. Trois politiques couvrent la plupart des besoins :

PolitiqueComportement sur une variable absente
Undefined (par défaut)Affiche une chaîne vide, ne lève rien
StrictUndefinedLève une UndefinedError, idéal pour détecter les oublis
DebugUndefinedRend un marqueur {{ nom }} visible, utile en développement
from jinja2 import Environment, StrictUndefined
env = Environment(undefined=StrictUndefined)

Avec StrictUndefined, toute variable manquante arrête le rendu et signale clairement le problème, plutôt que de produire une page silencieusement incomplète.

Pour utiliser Jinja2 dans un projet Python, il est essentiel de créer un environnement Jinja2. Cet environnement gère les templates et fournit une interface pour les charger et les rendre. Vous pouvez créer un environnement Jinja2 avec la classe Environment et spécifier où les templates doivent être trouvés (généralement dans un répertoire dédié).

Voici un exemple de base pour initialiser l'environnement :

from jinja2 import Environment, FileSystemLoader
# Initialiser l'environnement avec un dossier de templates
env = Environment(loader=FileSystemLoader('templates'))

Dans cet exemple, nous spécifions que les templates seront stockés dans un dossier nommé templates. Le FileSystemLoader permet à Jinja2 de rechercher les fichiers dans ce répertoire.

Une fois l'environnement configuré, vous pouvez charger un template à l'aide de la méthode get_template() et le rendre avec les données que vous souhaitez injecter. Voici un exemple simple :

# Charger le template
template = env.get_template('index.html')
# Rendre le template avec des données
html_rendu = template.render(titre="Page d'accueil", utilisateur={"nom": "Alice"})
print(html_rendu)

Dans cet exemple, nous chargeons le template index.html (qui se trouve dans le répertoire templates) et le rendons avec des données comme le titre de la page et l'objet utilisateur. Le contenu du template avec les données injectées est ensuite stocké dans la variable html_rendu et vous pouvez l'afficher ou l'enregistrer dans un fichier.

Il peut parfois être difficile de comprendre pourquoi une erreur se produit dans un template. Jinja2 n'a pas d'option debug sur son environnement, mais il fournit plusieurs leviers concrets pour diagnostiquer un rendu.

D'abord, Jinja2 intègre déjà le nom du template et le numéro de ligne fautif dans ses traces d'erreur Python. Lisez la pile d'appels : elle pointe la ligne exacte du template concerné, sans configuration particulière.

Ensuite, l'extension jinja2.ext.debug ajoute une balise {% debug %} qui affiche le contexte courant (variables disponibles et filtres) à l'endroit où vous la placez :

env = Environment(extensions=['jinja2.ext.debug'])
{% debug %}

Enfin, pour repérer les variables non résolues, rendez-les visibles avec DebugUndefined, ou faites-les échouer explicitement avec StrictUndefined (voir la gestion des erreurs ci-dessus). Ces options sont précieuses lors du développement, mais évitez DebugUndefined en production pour ne pas exposer la structure interne de vos données.

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Jinja2 sépare clairement la logique applicative et la présentation, c'est cette séparation qui rend le code maintenable sur le long terme.
  • Les variables, boucles et conditions couvrent 80 % des cas. Les macros et l'héritage de templates prennent le relais quand la duplication apparaît.
  • L'autoescape est désactivé par défaut. Activez-le explicitement dans tout contexte HTML pour éviter les XSS.
  • Les filtres personnalisés et les fonctions Python injectées étendent Jinja2 sans casser la séparation logique/présentation, c'est par là qu'on industrialise des templates de génération de configuration ou de documents.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn