Flask utilise le moteur de templates Jinja 2 par défaut. Ce dernier nous permet d'utiliser certaines méthodes dans les templates pour nous simplifier la vie : lien vers d'autres pages, import d'autres templates... Nous allons voir tout cela en détail dans ce chapitre.
Utilisez une méthode dans le template
Nous avons réussi à afficher le template index.html
ainsi que les feuilles de style. Mais nous pourrions faire mieux ! Le template n'est pas qu'un fichier HTML classique car vous avez accès à de nombreux objets qui vous faciliteront la vie.
Flask offre, par exemple, une méthode plutôt intéressante pour faire appel à des fichiers qui se trouvent dans le dossier static : url_for()
. Cette méthode est disponible dans le template.
Que fait-elle ? Elle génère une URL en fonction des paramètres transmis.
Le premier paramètre est le nom du dossier qui contient les fichiers que l'on souhaite lier. Le second, optionnel, est le nom d'un fichier. Par exemple :
url_for('static', filename='css/bootstrap/bootstrap.min.css')
Ceci générera le lien suivant : /static/css/bootstrap/bootstrap.min.css
.
Intégrez-la dans index.html
! Mais si vous vous contentez d'insérer la méthode telle quelle dans le template, elle sera interprétée comme du HTML pur.
Afin d'indiquer à Jinja 2 que vous insérez du Python, qui doit donc être interprété comme tel, entourez le code d'accolades. Comme ceci :
<div>
{{ url_for('static', filename='css/bootstrap/bootstrap.min.css') }} <!-- La phrase sera affichée dans la page -->
{% if blur: %}Je ne m'affiche que si "blur" est vrai.{% endif %}
{# ceci est un commentaire #}
</div>
Remplacez tous les liens de la page ! Oui je sais, il y en a beaucoup... :'(
Utilisez la méthode objets disponibles dans le template
La méthode url_for()
fait partie des objets disponibles dans le template. Voici les autres :
config
: dictionnaire comprenant la configuration de l'application (dans notre cas, les éléments qui se trouvent dansconfig.py
).request
: les détails de la requête reçue.session
: la session actuelle. Vous pouvez en effet stocker des valeurs dans la session pour la retrouver ultérieurement. Il s'agit d'une pratique très courante pour authentifier un utilisateur et se souvenir de son passage.g
: variables globales.get_flashed_messages()
: messages flash. Nous n'entrerons pas dans le détail de cette méthode mais je vous invite à lire la documentation pour en savoir plus.
Je vous propose d'utiliser la variable config
pour afficher le contenu de la variable FB_APP_ID
de config.py
:
window.fbAsyncInit = function() {
FB.init({
appId : '{{ config['FB_APP_ID'] }}',
cookie : true, // enable cookies to allow the server to access
// the session
xfbml : true, // parse social plugins on this page
version : 'v2.8' // use graph api version 2.8
});
}
...
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "https://connect.facebook.net/fr_FR/sdk.js#xfbml=1&version=v2.9&appId={{ config['FB_APP_ID'] }}";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
Si vous rafraîchissez la page, vous voyez maintenant apparaître le bouton 'Continuer avec Facebook' en bas de la page. Bravo !
Utilisez l'héritage
Passons maintenant à la page de résultats result.html
. Définissez une nouvelle vue :
@app.route('/result/')
def result():
return render_template('result.html')
En ouvrant result.html
, vous remarquez aisément qu'une partie du code se répète. Par exemple, les appels à l'API de Facebook. Vous avez mis à jour l'identifiant Facebook de l'index.html
mais vous devriez également en faire de même ici !
Mais que nenni, mon bon ami ! Jinja 2 nous permet de créer un template que l'on pourra étendre à volonté.
Étendre ? Tu veux dire, comme du linge ?
Je ne réagirai pas à cet essai de blague. Non, étendre comme une classe enfant qui hériterait d'une classe parent : vous pouvez changer les valeurs par défaut du template parent. C'est assez simple !
Créez un template base.html
qui contiendra le squelette de toutes nos pages : head
, footer
...
index.html
contiendra alors ce qui sera amené à changer dans le template d'origine : le contenu du body.
Indiquez à présent que vous souhaitez étendre base.html
dans le template result.html
. Vous pouvez faire cela en utilisant le mot-clé extends 'nomdutemplate.html'
:
result.html
{% extends 'base.html' %}
...
Si vous rechargez la page, vous verrez que le contenu du template base.html
s'affiche... mais pas celui de result.html
. Pourtant il y a bien du contenu dans ce dernier !
Vous vous en doutez, c'est normal... Le template parent agit un peu comme un texte à trou : vous devez indiquer les endroits qui seront amenés à changer.
Utilisez le mot-clé block nomdemonblock
comme ceci :
base.html
...
</nav>
{% block content %}{% endblock %}
<footer class="text-center">
...
Jinja 2 sait maintenant que du contenu peut être inséré à cet endroit.
Dans le template enfant, result.html
, englobez le contenu à envoyer dans le même bloc :
result.html
{% block content %}
<!-- Header -->
<header>
...
</script>
{% endblock %}
Je vous laisse en faire de même pour le template index.html
!
Passez des variables au template
Notre page de résultats est bien jolie mais il est grand temps de la rendre dynamique ! Autrement dit, de pouvoir changer les éléments suivants en fonction de l'utilisateur :
prénom
photo de profil
description
La méthode render_template()
, que nous utilisons dans la vue, est un peu spéciale. Vous pouvez lui donner en paramètres toutes les variables que vous souhaitez utiliser dans le template. Par exemple, le prénom :
views.py
@app.route('/result/')
def result():
return render_template('result.html',
user_name="Tom")
result.html
...
<h2 id="user_name">{{ user_name }}</h2>
...
Continuez ainsi en remplaçant la photo de profil et la description :
views.py
@app.route('/result/')
def result():
description = """
Toi, tu n'as pas peur d'être seul ! Les grands espaces et les aventures sont faits pour toi. D'ailleurs, Koh Lanta est ton émission préférée ! Bientôt tu partiras les cheveux au vent sur ton radeau. Tu es aussi un idéaliste chevronné. Quelle chance !
"""
return render_template('result.html',
user_name='Tom',
user_image=url_for('static', filename='tmp/cover_111823112767411.jpg'),
description=description)
result.html
...
<div class="col-sm-3 col-sm-offset-3">
<img class="img-responsive img-circle user_profile" src="{{ user_image }}" id="user_image">
</div>
<div class="col-sm-3">
<h2 id="user_name">{{ user_name }}</h2>
</div>
<div class="col-sm-12">
<p id="description">{{ description }}</p>
</div>
...
Faites-en de même pour la vue index
.
Importez le template
Vous touchez presque au but !
Avant de passer au prochain chapitre, refactorisez un peu les templates. Vous ne trouvez pas qu'une partie du template se répète ?
Cette partie est exactement la même entre index.html
et result.html
désormais :
<div class="card">
<div class="row">
<div class="col-sm-3 col-sm-offset-3">
<img class="img-responsive img-circle user_profile" src="{{ user_image }}" id="user_image">
</div>
<div class="col-sm-3">
<h2 id="user_name">{{ user_name }}</h2>
</div>
<div class="col-sm-12">
<p id="description">{{ description }}</p>
</div>
</div>
</div>
La seule différence est la classe blur
, présente dans l'index et non dans la page de résultats.
Je vous propose de passer la valeur de blur
dans la vue et de créer une structure conditionnelle dans le template :
views.py
@app.route('/')
@app.route('/index/')
def index():
...
return render_template('index.html',
user_name='Julio',
user_image=url_for('static', filename='img/profile.png'),
description=description,
blur=True)
result.html et index.html
<p id="description" {% if blur: %}class="blur"{% endif %}>{{ description }}</p>
Créez un nouveau template, card.html
, dans lequel vous collerez la portion de code qui se répète. Puis incluez-le dans result.html
et index.html
en utilisant la méthode include
.
index.html et result.html
<div class="col-lg-6 col-lg-offset-3">
{% include 'card.html' %}
</div>
Rechargez les pages et admirez le travail !
Récupérez le code du chapitre
Retrouvez le code de ce chapitre à cette adresse.
En résumé
Jinja est un moteur de template. Il permet d'écrire du code Python dans un template (en plus du HTML)
Pour indiquer à Jinja qu’on utilise des objets Python, il faut utiliser les syntaxes
{{ }}
,{% %}
ou{{# #}}
selon le code écritPour insérer des objets Python dans un template, il faut les passer en paramètres de la fonction
render_template
au niveau de la vueJinja permet d’étendre des templates grâce à l’héritage
Pour éviter de dupliquer du code, vous pouvez importer des templates HTML au sein d’un autre template
Bravo, les templates de votre projet utilisent désormais Jinja. Passons maintenant à la dynamisation de votre page de résultat.