Templates
Nos pages sont pour l'instant encore un peu tristounettes et basiques. Si nous voulons plus de richesse, il va falloir du HTML, voire du CSS. Mais générer tout ce contenu en python dans des f-strings peut vite devenir compliqué et tout aussi illisible qu'un programme en PHP :P.
Bottle (comme tous les autres frameworks) fournit des outils pour faciliter la création de pages : ce sont les templates.
Un template est un fichier qui contient du HTML et des balises spéciales pour intégrer des données dynamiques.
Plusieurs moteurs de templates existent, mais nous allons utiliser le moteur intégré de bottle, qui est très simple à utiliser et suffisant pour nos besoins.
Moteur de template SimpleTemplate
Bottle utilise le moteur de template SimpleTemplate (ou stpl). Le principe
du template est de pouvoir être "rendu" avec des données dynamiques, en
remplaçant les balises spéciales par les valeurs correspondantes.
from bottle import SimpleTemplate
tpl = SimpleTemplate('Hello {{name}}!')
print(tpl.render(name='World')) # affiche u'Hello World!'
assert tpl.render(name='World') == 'Hello World!'
ou bien
from bottle import SimpleTemplate
tpl = SimpleTemplate('Hello {{name}}!')
dico = {'name': 'World'}
print(tpl.render(**dico)) # affiche 'Hello World!'
assert tpl.render(**dico) == 'Hello World!'
Référence : https://bottlepy.org/docs/dev/stpl.html
En résumé, un template est une sorte de f-string boostée.
Utilisation
Pour utiliser les templates, il faut d'abord importer la fonction template de
bottle.
from bottle import template #...
On peut alors utiliser les templates de deux manières :
- soit en les définissant dans des chaînes de caractères
- soit en les définissant dans des fichiers séparés, par défaut placés dans le
répertoire
views1.
Templates dans le code
HOMEPAGE = """
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page d'accueil</title>
</head>
<body>
<h1>Page d'accueil</h1>
<p>Hello {{ name }}</p>
</body>
</html>
"""
@route('/')
def hello():
return template(HOMEPAGE, name="World")
Templates dans des fichiers
Fichier views/index.html (télécharger le code) :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Page d'accueil</title>
</head>
<body>
<h1>Page d'accueil</h1>
<p>Hello {{ name }}</p>
</body>
</html>
Fichier main.py (télécharger le code) :
@route('/')
def index():
return template('index.html', name="World")
Syntaxe
Expressions en ligne
Les expressions en ligne sont entourées de {{ et }}. Elles peuvent contenir
n'importe quelle expression python (des variables, des opérations arithmétiques,
des appels de fonctions, etc...) à partir du moment où cette expression renvoie
une chaîne de caractères ou quelque chose qui peut être converti en chaîne de
caractères.
<p>Hello {{ name }}</p>
<p>2 + 2 = {{ 2 + 2 }}</p>
<p>Le résultat est {{ "positif" if a > 0 else "negatif" }}</p>
L'expression est évaluée lors du rendu et a accès à tous les arguments passés au
template (le contexte). Les caractères HTML spéciaux sont échappés pour
éviter les injections XSS. Par exemple, si name contient
<script>alert("Hello")</script>, le code rendra
<script>alert("Hello")</script> et non pas le script.
Si vous voulez afficher le code HTML tel quel (sans échappement), il faut
utiliser la balise {{! et }} :
<p>Hello {{! name }}</p>
Code python inclus
Il est possible d'inclure des blocs de python dans le template. Les lignes de
code commencent par %, ou bien <% et %> pour les blocs de code. Le code
python "inclus" suit la même syntaxe que le python normal, avec une exception
notable : l'indentation est ignorée, pour permettre d'écrire du html
joliment, tous les blocs qui nécessiteraient une indentation en python doivent
être fermés explicitement avec un mot clef end :
% name = "Bob" # une ligne de code python
<p>Du contenu HTML</p>
<%
# Un bloc de code python
name = name.lower().strip()
%>
<p>Encore du html</p>
<p>Bonjour {{ name }}</p>
% # Conditions
% if name == "Bob":
<p>Bonjour Bob</p>
% else:
<p>Bonjour autre</p>
% end
% # Boucle for range
<ul>
% for i in range(1, 11):
<li>{{ i }}</li>
% end
</ul>
% # Boucle sur un dictionnaire
% # à appeler avec `template('index.html', dico={'a': 1, 'b': 2})`
<ul>
% for key, value in dico.items():
<li>{{ key }} : {{ value }}</li>
% end
</ul>
% # Fonctions, bloc de code ...
<%
def add(a, b):
return a + b
end
%>
<p>2 + 2 = {{ add(2, 2) }}</p>
Fonctions de template
Quelques fonctions utiles sont directement utilisables dans les templates :
include(sub_template, **context): inclut un sous-template dans le template courant. Le contexte est passé au sous-template.setdefault(name, default): définit une valeur par défaut pournamedans le contexte. Sinamen'est pas défini, il sera défini avec la valeur dedefault.defined(name): renvoieTruesi le nom est défini dans le contexte,Falsesinon.get(name, default=None): renvoie la valeur denamedans le contexte, oudefaultsinamen'est pas défini.
include est très utile pour éviter de répéter du code dans plusieurs
templates. Par exemple, si vous avez un template header.html qui contient
le code HTML d'en-tête de votre site, vous pouvez l'inclure dans tous vos
templates.
setdefault est aussi très utile pour définir des valeurs par défaut pour les
variables dans le contexte, de manière à pouvoir appeler les templates en fournissant
juste les variables qui vous intéressent.
% setdefault('page_title', "Page d'accueil")
% setdefault('erreur', None)
% # ...
<h1>{{page_title}}</h1>
% if erreur:
<p class="erreur">{{ erreur }}</p>
% end
# ... appels possibles de ce template
return template('tpl08.html', page_title="Page d'accueil", erreur="Erreur de saisie")
return template('tpl08.html', page_title="Page d'accueil")
return template('tpl08.html')
Liste complète des fonctions de template : https://bottlepy.org/docs/dev/stpl.html#template-functions
En pratique
Vue index / vue hello_user
Créer une vue index qui affiche une page d'accueil HTML. Vous pouvez
utiliser du CSS pour "décorer" votre page, le css devant pour l'instant être
inclus directement dans le HTML. Ajoutez un lien vers la route /hello/Bob
(ou autre) pour tester la route hello_user.
<head>
<!-- .... -->
<style>
body {
background-color: #f0f0f0;
color: #333;
font-family: Arial, sans-serif;
}
h1 {
color: #007bff;
}
</style>
</head>
Modifier la vue hello_user (route /hello/<name>) pour qu'elle affiche un
message de bienvenue joliment présenté (en utilisant un template
hello_user.html). Rajouter un lien vers la page d'accueil.
Correction
Fichier main.py (télécharger le code) :
from bottle import route, run, template
# Page d'accueil
@route('/')
def index():
return template('index.html')
# Bonjour personnalisé
@route('/hello/<name>')
def hello_user(name):
return template('hello_user.html', name=name)
Fichier views/index.html (télécharger le code) :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Page d'accueil</title>
<style>
body {
background-color: #f0f0f0;
color: #333;
font-family: Arial, sans-serif;
}
h1 {
color: #007bff;
}
</style>
</head>
<body>
<h1>Page d'accueil</h1>
<p>Bienvenue sur notre site !</p>
<p><a href="/hello/Bob">Dire bonjour à Bob</a></p>
</body>
</html>
Fichier views/hello_user.html (télécharger le code) :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bonjour {{ name }}</title>
<style>
body {
background-color: #f0f0f0;
color: #333;
font-family: Arial, sans-serif;
}
h1 {
color: #007bff;
}
</style>
</head>
<body>
<h1>Bonjour {{ name }}</h1>
<p>Bienvenue sur notre site !</p>
<p><a href="/">Retour à la page d'accueil</a></p>
</body>
</html>
Table de multiplication
Créer une route /tabmul/<nb:int>, associée à un template tabmul.html, qui
affiche la table de multiplication de nb, et qui propose une liste de liens
permettant d'afficher les tables de multiplication de 1 à 10.

Correction
Fichier main.py (télécharger le code)
#...
# Table de multiplication
@route('/tabmul/<nb:int>')
def tabmul(nb):
return template('tabmul.html', nb=nb)
Fichier views/tabmul.html (télécharger le code)
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Table de multiplication de {{ nb }}</title>
</head>
<body>
<h1>Table de multiplication de {{ nb }}</h1>
<ul>
% for i in range(1, 11):
<li>{{ nb }} x {{ i }} = {{ nb * i }}</li>
% end
</ul>
<h2>Autres tables de multiplication</h2>
<ul>
% for i in range(1, 11):
<li>Table de <a href="/tabmul/{{ i }}">{{ i }}</a></li>
% end
</ul>
<p><a href="/">Retour à la page d'accueil</a></p>
</body>
</html>
On peut modifier le chemin de recherche des templates en modifiant la variable TEMPLATE_PATH (from bottle import TEMPLATE_PATH),
qui contient une liste de chemins dans lesquels bottle cherchera les templates. Par défaut, les chemins
sont : “./” et “./views”, c’est à dire le répertoire courant (celui dans lequel on a lancé le serveur
qui n’est pas forcément celui du fichier python du serveur) et le sous-répertoire “views” du répertoire courant.
Pour être ne plus dépendre du répertoire courant, il est pratique de modifier TEMPLATE_PATH
pour lui indiquer comme chemin de recherche le répertoire du fichier python du serveur. Cela peut
se faire à l’aide de la ligne
from os.path import dirname
TEMPLATE_PATH.insert(0, join(dirname(__file__), "xxxxx")
Cette ligne récupère le répertoire de __file__ (une variable spéciale de python qui contient le
chemin absolu vers le fichier dans lequel elle se trouve), et insère cette valeur au début de
TEMPLATE_PATH (= ce sera le premier chemin recherché), en ayant joint ce répertoire avec le
répertoire “xxxxx” dans lequel on veut chercher les templates.