Pour cette session nous utiliserons un projet déjà existant. Veuillez récupérer ZIP l’archive accessible à l’URL suivante tp_templates_start.zip.
Ce projet contient 3 vues:
- view_a
- view_b
- view_c
qui ont le même code et font appel à des templates dont le code est très similaire, et donc affichent toutes une page web similaire, dont seule une partie du code HTML change. Jetez un oeil à ces fonctions et à ces templates.
Il y a clairement duplications de code, ce qui contrevient au principe
Do not repeat
yourself
recherché lors du développement de logiciels.
Dans cette session, nous verrons comment utiliser des fonctions avancées du moteur de templates “Jinja2” pour limiter les répétitions de code.
Templates Jinja avancées
Pour cet exemple, nous utiliserons du code CSS intégré directement
dans la page entre deux balises <style>
. Cette pratique est
généralement découragée, mais simplifiera la compréhension dans ce
cas.
Héritage de templates
Introduction
On peut avec Jinja définir des blocs à l’intérieur d’une template avec
les instructions block
:
{% block nom_du_bloc %}
ancienne valeur
{% endblock %}
On peut ensuite de définir une template “filles”, héritant d’une template mère, et qui modifiera la valeur d’un des blocs de la template mère. Cela se fait en:
- déclarant une nouvelle template (la template fille)
- indiquant que la template fille étend la template mère avec l’instruction
extends
:{% extends "template_mere.jinja2" %}
- en redéfinissant le bloc dans la template fille:
{% block nom_du_bloc %} nouvelle valeur {% endblock %}
Exercice
Définir un nouveau fichier template templates/layout.html.jinja2
, et
y mettre le code suivant:
<html>
<head>
<title>{{ title }}</title>
<!-- <style> est une balise qui permet d'inclure du code CSS dans une page web,
sans définir un fichier externe de style CSS -->
<style>
.active {
color: red;
font-weight: bold;
}
</style>
</head>
<body>
<header>
<a href="/view/a" class="active">Template A</a> |
<a href="/view/b">Template B</a> |
<a href="/view/c">Template C</a>
<hr>
</header>
{% block body %}
{% endblock %}
<footer>
<hr>
Flask SAR - 2019
</footer>
</body>
</html>
et modifier les templates:
- templates/template_a.html.jinja2
- templates/template_b.html.jinja2
- templates/template_c.html.jinja2
pour qu’elles étendent la template layout.html.jinja2
et adapter le message affiché au fichier de template:
{% extends "layout.html.jinja2" %}
{% block body %}
<p>Template A</p>
{% endblock %}
Nous avons factorisé le code des templates. Cependant, l’affichage de la page active dans la barre du haut n’est pas bon : la template A est marquée sélectionnée, même si nous naviguons sur les pages des templates B et C. Nous verrons dans la section suivante comment gérer cet affichage avec des variables Jinja.
Manipulation de variables
Nous allons maintenant voir comment ajouter des variables jinja2 dans les templates filles, afin d’indiquer à la template “racine” quelle est la page active ainsi que le nom de la page. Ces variables seront prises en compte dans le code de la template layout.html.jinja2
.
Premièrement, modifier les templates:
- templates/template_a.html.jinja2
- templates/template_b.html.jinja2
- templates/template_c.html.jinja2
pour qu’elles aient la structure suivante:
{% extends "layout.html.jinja2" %}
{% set title = "template A" %}
{% set active_page = "view_a" %}
{% block body %}
<p>Template A</p>
{% endblock %}
Nous allons maintenant modifier la template layout.html.jinja2
, afin
qu’elle prenne en compte les précédentes variables:
<html>
<head>
<title>{{ title }}</title>
<style>
.active {
font-weight: bold;
color: red;
}
</style>
</head>
<body>
<header>
<a href="{{ url_for("view_a") }}" {% if active_page == "view_a" %}class="active"{% endif %}>Template A</a> |
<a href="{{ url_for("view_b") }}" {% if active_page == "view_b" %}class="active"{% endif %}>Template B</a> |
<a href="{{ url_for("view_c") }}" {% if active_page == "view_c" %}class="active"{% endif %}>Template C</a>
<hr>
</header>
{% block body %}
{% endblock %}
<footer>
<hr>
Flask SAR - 2019
</footer>
</body>
</html>
En allant sur l’URL http://127.0.0.1:5000/view/b, vous devriez obtenir le résultat suivant:
Macro
Jinja fournit un mécanisme de macros qui permet de factoriser certaines instructions sous la forme d’une fonction. Leur intérêt est de limiter les répétitions de code.
Dans l’exemple précédent, nous avions ce code:
<a href="{{ url_for("view_a") }}" {% if active_page == "view_a" %}class="active"{% endif %}>Template A</a> |
<a href="{{ url_for("view_b") }}" {% if active_page == "view_b" %}class="active"{% endif %}>Template B</a> |
<a href="{{ url_for("view_c") }}" {% if active_page == "view_c" %}class="active"{% endif %}>Template C</a>
qui posait les problèmes suivants:
- répétition trois fois du même code
- la structure de contrôle
if
est mise sur une seule ligne, afin de rendre le code plus concis, mais au final l’ensemble est moins lisible.
La définition d’une macro dans le fichier layout.html.jinja2
qui
prend deux paramètres. Ces paramètres sont réutilisables ensuite dans
le corps de la macro:
{% macro set_active_if(variable, name) %}
{% if variable == name %}class="active"{% endif %}
{% endmacro %}
La macro étant définie, elle est utilisable en l’appelant comme une fonction pyth:
<a href="{{ url_for("view_a") }}" {{ set_active_if(active_page, "view_a") }}>Template A</a> |
<a href="{{ url_for("view_b") }}" {{ set_active_if(active_page, "view_b") }}>Template B</a> |
<a href="{{ url_for("view_c") }}" {{ set_active_if(active_page, "view_c") }}>Template C</a>
Le code est maintenant plus concis, et la condition if
est plus
lisible. Vous trouverez plus d’informations sur les macros avec le
lien suivant
http://jinja.pocoo.org/docs/2.10/templates/#macros.
Ajouter des fichiers CSS et Javascript avec Jinja
Les fichiers CSS et Javascript sont généralement des fichiers statiques, c’est-à-dire que leur contenu ne change pas en fonction des requêtes des utilisateurs.
Avec Flask, il faut mettre les fichiers CSS et Javascript dans le
dossier static
. Les fichiers présents dans le dossier static
sont
publics, et ont chacun une URL.
Il est possible de récupérer l’URL d’un fichier statique avec la
fonction url_for
comme dans l’exemple suivant:
from flask import url_for # a mettre en debut de fichier
fichier_css_url = url_for("static", filename="css/fichier.css")
print(fichier_css_url)
Sous dossiers de ressources
Créer trois dossiers css
, js
et img
dans le dossier static
d’un projet Flask.
Créer le fichier static/css/style.css
avec le code css suivant:
.logo {
width: 400px;
}
Créer le fichier static/js/script.js
avec le code suivant:
(function update_caption() {
let caption_text = "logo IMT Atlantique";
document.getElementById("caption_logo").innerText = caption_text;
})();
Ensuite, faire les actions suivantes:
- aller à cette page https://www.imt-atlantique.fr/fr/l-ecole/logotypes-imt-atlantique
- télécharger un des logos, le mettre dans le dossier
static/img/
et le renommerlogo.jpg
(adapter l’extension de fichier à celle du logo téléchargé
Maintenant que nous avons préparé les resources statiques pour cet
exercice, créez une template jinja
templates/static_resources.html.jinja2
qui contient le code suivant:
<html>
<head>
<title>Static resources with Flask</title>
<link rel="stylesheet" href="{{ url_for("static", filename="css/style.css") }}"/>
</head>
<body>
<img class="logo" src="{{ url_for("static", filename="img/logo.jpg") }}"/>
<span id="caption_logo"></span>
</body>
<script src="{{ url_for("static", filename="js/script.js") }}"></script>
</html>
et appeler cette template depuis une nouvelle fonction Python:
@app.route('/static_resources_view')
def static_resources_view():
return render_template("static_resources.html.jinja2")
En allant sur le lien http://127.0.0.1:5000/static_resources_view, vous devriez obtenir le résultat suivant:
En affichant le code source de la page, on peut constater que le texte sous le logo de l’IMT n’apparait pas dans le code HTML:
En effet, nous l’avons fait apparaitre en modifiant le DOM de la page Web avec Javascript. Nous verrons dans une session future comment faire des modifications avancées avec Javascript.
Correction
Vous pouvez récupérer une archive ZIP tp_templates_correction.zip contenant la correction de cette séance.