Utilisation des Templates Jinja2
Mise à jour :
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.
Installation de Jinja2
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.
Utilisation d’un environnement virtuel
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 :
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 :
- Sous Linux/macOS :
source env/bin/activate
- Sous Windows :
env\Scripts\activate
Une fois l’environnement activé, vous pouvez installer Jinja2 sans affecter d’autres projets Python sur votre machine :
Installation avec pip
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 :
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 :
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 :
pip install Jinja2==3.0.3
Cela garantit que la version de Jinja2 utilisée dans votre projet ne changera pas, même si une nouvelle version est publiée.
Vérification de l’installation
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 :
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
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 :
poetry add Jinja2
De même, avec Pipenv :
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.
Mise à jour de Jinja2
Enfin, si vous avez besoin de mettre à jour Jinja2 vers une version plus récente, il suffit d’exécuter la commande suivante :
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.
Syntaxe de base des templates Jinja2
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.
Commentaires dans les templates
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.
Affichage de variables
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.
Variables non définies
Dans certains cas, une variable que vous essayez d’afficher dans un template
peut ne pas être définie. Par défaut, Jinja2 ne génère pas d’erreur pour les
variables inexistantes, mais affiche une chaîne vide à la place. Cependant, il
est possible de lever une erreur si une variable manquante est critique en
utilisant l’option {{ variable | required }}
.
Utilisation des expressions Jinja2
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>
Filtres pour manipuler les données
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 par défaut si la variable n’est pas définie ou est vide.
<p>{{ utilisateur.nom|default("Invité") }}</p>
Si utilisateur.nom est vide ou non défini, Jinja2 affichera “Invité”.
- 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.
Boucles et conditions
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.
Conditions dans Jinja2
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é.
Conditions multiples
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.
Conditions complexes avec opérateurs
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.
Conditions avec tests prédéfinis
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.
Utilisation des boucles
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>
Boucles avec index et variables spéciales
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.
Boucles avec else
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
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.
Macros Jinja2
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.
Introduction aux macros
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>
Passage d’arguments aux macros
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
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
:
{% 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 :
{% 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.
###4 Macros avec des blocs de contenu
Il est possible de passer non seulement des valeurs primitives (comme des chaînes ou des nombres) aux macros, mais également des blocs de contenu. Cela permet de créer des composants réutilisables avec du contenu personnalisé. Voici un exemple de macro qui accepte un bloc de contenu HTML en tant qu’argument :
{% macro carte(titre, contenu) %}<div class="carte"> <h3>{{ titre }}</h3> <div class="contenu"> {{ contenu() }} </div></div>{% endmacro %}
Pour utiliser cette macro, vous passez un bloc de contenu sous forme de fonction anonyme :
{% from "macros.html" import carte %}
{{ carte('Mon Titre', lambda: "<p>Voici le contenu de la carte.</p>") }}
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>
Cela vous permet de définir des composants réutilisables et de les personnaliser avec des blocs de contenu complexes.
Gestion des espaces blancs
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.
Héritage des templates
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.
Introduction à l’héritage des templates
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
:
<!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>© 2024 Mon Site</p> </footer></body></html>