Templating avec Cheetah

Cheetah est un moteur de Templating écrit en Python. Il est simple d'utilisation et rapide. Je l'utilise notamment pour créer des pages HTML. Des nombreux framework web l'utilisent dont notamment l'excellent web.py, qui permet de rendre les templates directement pour peu que Cheetah soit installé.

Je ne vais pas m'étendre sur les fonctionnalité basiques de Cheetah, vous trouverez de très bonnes introductions sur le sujet sur cette page. Ici on va plutôt aborder des techniques un peu avancées pour les problématiques liées aux pages HTML.

La problématique principale du Templating HTML c'est qu'il faut permettre de générer dynamiquement les pages sans avoir à chaque fois à recopier tout le design.

Une première approche consiste à créer 2 fichiers :

header

<html>
<head>
<title>titre</title>
</head>
<body>

footer

</body>
</html>

Puis on crée le fichier contenant le corps de notre page

page.tmpl

#include "header"
tada!
#include "footer"

Et enfin pour finir on appelle Cheetah pour rendre la page : PYTHONPATH=. cheetah fill page.tmpl (le PYTHONPATH permet de dire à Cheetah ou il doit aller chercher ses fichiers templates). Cela produit un fichier page.html qui contient :

<html>
<head>
<title>titre</title>
</head>
<body>
tada!
</body>
</html>

Cette approche a l'avantage d'être très simple à mettre en oeuvre. Apparemment elle est utilisée dans pas mal de cas. Toutefois elle n'est pas très puissante. Les possibilités offertes sont plus que limitées. Par exemple si l'on souhaite avoir une page de base puis rajouter des menus sur certaines catégories de pages, on est obligé d'inclure à la main les menus dans chaque pages.

Heureusement Cheetah gère l'héritage entre templates. Voici comment ça se passe. Soit le fichier suivant :

base.tmpl

#def title: mon titre a moi
<html>
<head>
<title>$title</title>
</head>
<body>
$content
</body>
</html>

Dans ce cas, on a un template de base qui a 2 paramètres : $title et $content. $title étant déjà défini dans base.tmpl il n'est pas obligatoire de le redéfinir dans les templates fils. En revanche $content doit être défini dans chacun des templates qui étendent base.tmpl. Par exemple :

page1.tmpl

#extends base
#block content
Ca rox cheetah!
<a href='/'>test</a>
#end block

Ici on définit le bloque content, qui remplacera $content dans base.tmpl. On lance donc PYTHONPATH=. cheetah fill -p page1.tmpl et on obtient :

<html>
<head>
<title>mon titre a moi</title>
</head>
<body>
Ca rox cheetah!
<a href='/'>test</a>

</body>
</html>

Ensuite si l'on souhaite créer une autre page qui va elle redéfinir le titre c'est très simple :

page2.tmpl

#extends base
#def title: un autre titre
#block content
Ca rox cheetah!
<a href='/'>test</a>
#end block

Notez bien qu'ici def et block sont utilisé pour faire la même chose. Ces deux syntaxes sont équivalentes (voir la documentation de Cheetah pour plus d'informations).

Et maintenant on se heurte à un petit problème, si l'on souhait rajouter un menu à la page, il va falloir utiliser la variable $content, et du coup on ne peut plus utiliser cette variable dans les pages qui descendent ce template avec un menu ...

Pour éviter ce genre de problème on peut définir des bloques autour des zones qui sont susceptibles d'être modifiées par d'autres templates. Ainsi on modifie base.tmpl comme ceci :

#def title: mon titre a moi
<html>
<head>
#block head
<title>$title</title>
#end block head
</head>
<body>
#block body
$content
#end block body
</body>
</html>

Ainsi, quand vous souhaitez redéfinir certaines parties de votre page, il suffit d'utiliser les bloques que vous avez définis avant. Ainsi les anciens bloques seront remplacés.

Par exemple, si on souhaitait rajouter un menu dans la page précédente il suffirait de faire :

menu.tmpl

#extends base
#block body
<body>
<div class='menu'>
<ul>
<li>menu1</li>
<li>menu2</li>
</ul>
</div>
$content#slurp
</body>
#end block

Et hop! maintenant toutes les pages qui descendent de menu.tmpl contiendront le menu. Ce n'est pas révolutionnaire dans le concept. Mais c'est bien sympathique quand même!