Templates Django

Un template c'est quoi?

Les templates (en français on utilise les mots "patron" ou "gabarit") est un moyen de séparer le contenu d'un site internet au code. Ainsi chaque coeur de métier peut se concentrer sur ses besoins. Pour faire simple tout ce qui est HTML se trouve dans les templates. Ainsi les graphistes qui ne connaissent rien à la programmation python par exemple peuvent travailler en toute liberté sans prendre de risque de perturber un projet. Et récipriquement cela empêche les programmeurs de faire des bêtises au niveau du rendu. Finalement chacun est responsable de la partie du site qui le concerne. C'est un point essentiel pour le travail collaboratif.

Le template de Django

Comme à son habitude Django a choisi le moteur de template le plus efficace et puissant. Facile à prendre en main, il est en plus très complet.

Exemple d'utilisation

Nous avons vu dans des chapitres précédents qu'un template peut contenir des variables. Il est donc possible d'afficher des variables à conditions de lui indiquer. Ces variables sont un contexte que la vue lui envoie.

eboutique/urls.py

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from django.conf.urls import patterns, include, url
from backoffice.views import *

urlpatterns = patterns('',
    url(r'^product/$', ProductView.as_view()),
)

La vue:

backoffice/views.py

#!/usr/bin/python
#-*- coding: utf-8 -*-

from django.views.generic import *

class ProductView(TemplateView):
   
    template_name="backoffice/templates/product.html"
    
    def get_context_data(self, **kwargs):
        context = super(ProductView, self).get_context_data(**kwargs)
        context['firstname'] = "olivier"
        context['lastname'] = "engel"
        context['towns'] = ["Mulhouse", "Strasbourg", "Marseille"]
        context['data'] = {"age": 30, "genre": "male", "hobbies": "python"}
        context['years'] = (1900, 2000, 2014)
        return context

Dans cet exemple nous avons utilisé une simple variable en chaine de caractères, une liste, un dictionnaire et un tuple. Voyons maintenant comment récupérer les informations dans le template:

backoffice/templates/product.html

<p>Bonjour {{firstname}} {{lastname}}</p>

<ul>
{% for town in towns %}
    <li>{{town}}</li>
{% endfor %}
</ul>

<p>Age: {{data.age}}</p>

<ul>
{% for year in years %}
    <li>{{year}}</li>
{% endfor %}
</ul>

Voyons le résultat:

A noter que les morceaux de code contenus dans les caractères {% %} sont executés par Django mais ne sont pas affichés. On les nomme des "templates tags".

Lire les attributs et les méthodes d'un objet

La logique est la même pour la lecture des objets python:

Notre objet:

class Obj(object):
    
    def __init__(self, firstname, lastname, age):
        
        self.firstname = firstname
        self.lastname  = lastname
        self.age       = age
    
    def getAge(self):
        
        return self.age

Notre vue:

class ProductView(TemplateView):
   
    template_name="backoffice/templates/product.html"
    
    def get_context_data(self, **kwargs):
        context = super(ProductView, self).get_context_data(**kwargs)
        context['obj'] = Obj("Olivier", "engel", 30) 
        return context

Et le template:

<p>{{obj.firstname}} {{obj.lastname}} - {{obj.getAge}} ans</p>

Comparaisons

Vous pouvez comparer les variables et proposer une action en fonction de leurs valeurs:

def get_context_data(self, **kwargs):
    context = super(ProductView, self).get_context_data(**kwargs)
    context['firstname'] = "olivier"
    context['age'] = 30 
    return context
{% if firstname == "olivier" %}
OLIVIER
{% else %}
QQUN D'AUTRE
{% endif %}

Il est possible de comparer une valeur numérique avec >, >=,

{% if age >= 30 %}
VIEUX
{% else %}
JEUNE
{% endif %}

IN et NOT IN

Vous pouvez vérifier la présence d'une clé:

def get_context_data(self, **kwargs):
    context = super(ProductView, self).get_context_data(**kwargs)
    context['info'] = {"age": 30}
    return context
{% if "age" in info %}
YES
{% else %}
NO
{% endif %}

Les filtres templates

Vous pouvez executer des fonctions (filtres) sur des variables au niveau du template.

Dans l'exemple suivant nous appellons une variable qui n'existe pas. Nous prévoyons dans le template un texte qui s'affichera dans le cas où cette variable n'existe pas:

{{info.test|default:"Pas d'info"}}

Vous pouvez églament mettre un texte en capital:

{{ma_variable|upper}}

Ou changez le format de la date:

context['ma_date'] = timezone.now()
{{ma_date|date:"Y-n-j"}}

Retournera:

2014-9-10

Les formats de date possibles:

bMois sur 3 lettres. exemple: 'jan'
cISO 8601 format. exemple: 2014-01-02T10:30:00.000123+02:00
djour du mois sur 2 chiffres. exemple: '01' à '31'
Djour de la semaine sur 3 lettres. exemple: "Ven"
eNom de la timezone
EMois local
fheure. exemple: '12:45'
FMois textuel. exemple: 'Janvier'
gHeure format 12H. exemple: '1' à '12'
GHeure format 24H. exemple: '0' à '23'
hHeure format 12H avec 0. exemple: '01' à '12'
HHeure format 24H avec 0. exemple: '01' à '23'
iMinutes avec 0. exemple: '00' à '59'
jJour du mois sans 0. exemple: '1' à '31'
lJour de la semaine textuel. exemple: 'Vendredi'
LBooléen pour savoir si c'est une année bissextile.
mMois avec 0. exemple: '01' à '12'
MMois sur 3 caractères. exemple 'JAN'
nMois sans les 0. exemple: '1' à '12'
NAbbréviation du moins. exemple: 'Jan.', 'Fév.'
ODifférence d'heure avec le temps Greenwich
rFormat de date RFC 2822. exemple: 'Thu, 21 Dec 2014 16:01:07 +0200'
sSecondes avec 0. exemple: '00' à '59'
tNombre de jours dans le mois. exemple: '28' à '31'
uMicrosecondes. exemple: '0' à '999999'
Utimestamp. Nombre de secondes écoulées depuis 01/01/1970
wJour de la semaine en chiffre. '0' pour dimanche et '6' pour samedi
WNuméro de la semaine. exemple: '1' à '53'
yAnnée sur deux chiffres. exemple: '14'
YAnnée sur 4 chiffres. exemple: '2014'
zJour de l'année. exemple: '0' à '365'

Les filtres de base

add{{variable|add:2}} ajoute 2 à la valeur initiale. La variable peut être une liste, tout comme la valeur de add.
addslashes{{variable|addslashes}} ajoute des slashes. exemple: "Ajourd\'hui"
capfirst{{variable|capfirst}} Premier caractère en capital. Exemple: "Olivier"
center{{variable|center:"15"}} Centre une variable avec des espaces. "       Olivier       "
cut{{variable|cut:"-"}} Supprime dans la variable le caractère indiqué.
dateformat date. Voir exemple plus haut
default{{variable|default:"Rien"}} Affiche un texte par défaut si la variable n'existe pas.
default_if_none{{varaible|default_if_none:"Rien"}}} Affiche un texte par défaut si la variable vaut None et uniquement None
dictsort{{variable|dictsort:"name"}} Tri un dictionnaire par la clé indiqué. Dans notre cas ce sera trié par "name"
dictsortreversedidem que dictsort mais en sens inverse
divisibleby{{variable|divisibleby:"2"}} retourne True si la variable est divisble par "2"
escapeconvertit les caractères spéciaux en entité HTML
escapejsConvertit les caractères pour les utiliser dans des chaines de caractères javascript
filesizeformatRend plus lisible une taille de fichier. Exemple: 123456789 affichera 117.7 MB
firstRetourne le permier item d'une liste.
floatformat{{variable|floatformat:3}} indique le nombre de décimales à afficher.
iriencode{{variable|iriencode}} Si la valeur est "?test=1&me=2", la sortie sera "?test=1&me=2".
join{{variable|join:"-"}} transforme une liste en string. Exemple: ['a', 'b'] devient "a-b"
lastretourne le dernier item d'une liste
lengthTaille d'une liste/string
length_is{{variable|length_is:"2"}} retourne True si la taille de l'élément est 2
linebreaksRemplace les retours à la ligne par la balise br. Chaque nouvelle ligne est encadrée par la balise p
linebreaksbrRemplace les retours à la ligne par la balise br.
linenumbersChaque ligne commence par un numéro de ligne
ljust{{variable|ljust:"10"}}Alignement à gauche
lower{{variable|lower}} Transforme les caractères en miniscules
make_list{{variable|make_list}} Transforme la variable en liste. Exemple 123 devient ['1', '2', '3']
pluralize{{variable|pluralize:"y,ies"}} définit la fin du mot au pluriel. Exemple: cherry, cherries
random{{variable|random}} retourne un item au hasard d'une liste
rjust{{variable|rjust:"50"}} aligne la valeur de la variable sur 50 caractères.
safe{{variable|safe}} indique qu'on peut écrire le contenu de la variable tel quel.
slice{{variable|slice:"2"}} retourne une partie de la liste. exemple [1, 2, 3] devient [1, 2]
slugify{{variable|slugify}} slugifie une chaine de caractère. exemple: "Je m'appelle Olivier" devient "je-m-apelle-olivier"
striptags{{variable|striptags}} supprimer les balises [X]HTML
time{{variable|time:"H:i"}} affiche un format heure défini
timesince{{variable|timesince:variable2}} variable2 est optionel. Permet d'afficher le temps écoulé en texte entre deux dates. Si aucune date n'est renseigné pour variable2, now() sera utilisé.
timeuntil{{variable|timeuntil:variable2}} même logique que timesince, mais ici c'est le temps jusqu'à ce que.
title{{variable|title}} Ajoute une majuscule à chaque mot de la chaine, le reste en minuscule. exemple: "Bonjour toi TU vas bien?" devient "Bonjour Toi Tu Vas Bien?"
truncatechars{{variable|truncatechars:3}} permet de couper l'affichage d'une variable. exemple "Bonjour" devient "Bonj..."
truncatechars_html{{variable|truncatechars_html|10}} même logique que truncatechars sauf qu'il ne prend pas en compte les balises HTML
truncatewords{{variable|truncatewords:2}} n'affiche que le nombre de mot indiqué. "Bonjour toi tu vas bien" devient "Bonjour toi"
truncatewords_html{{variable|truncatewords_html:2}} même logique que truncatewords mais ne prend pas en compte les balises HTML
unordered_list{{variable|unordered_list}} convertit une liste en liste non ordonnées avec balises ul et li. Si la liste inclu d'autres listes, cela créera un arbre avec une arborescence.
upper{{variable|upper}} convertit les caractères en majuscules
urlencode{{variable|urlencode}} encode une valeur pour qu'elle soit utilisable dans une URL
wordcount{{variable|wordcount}} retourne le nombre de mot dans la variable
wordwrap{{variable|wordwrap:"4"}} permet de limiter la largeur en caractère. exemple "Bonjour toi?" devient
"Bonj
our
toi "
yesno{{variable|yesno:"yeap,no,pi être"}} change la valeur de True par yeap, False par no et None par maybe

Ecrire des commentaire

vous pouvez écrire des commentaires (texte non visible par l'utilisateur) comme ceci:

{# Ceci est un commentaire #}

Autoescape

Vous pouvez automatiser l'échappement comme ceci:

{% autoescape on %}
    Bonjour {{ name }}
{% endautoescape %}

Créer des filtres template personnalisés

Il est possible de créer ses propres filtres. Pour cela, vous devez créer le package "templatetags" dans le dossier de votre app avec un fichier que nous appellerons perso.py :

tree /home/olivier/eboutique

eboutique
├── backoffice
│   ├── templatetags
│   │   ├── __init__.py
│   │   └── perso.py
├── eboutique
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

Editons le fichier perso.py :

from django import template
from django.template.defaultfilters import stringfilter

register = template.Library()

@register.filter
def add_value(value, arg):
    return value + int(arg) 
    
@register.filter
def replace_value(value, arg):
    return arg 

Utilisons-les dans notre template:

{% load perso %}

{{number|add_value:"12"}}
{{number|replace_value:"bonjour"}}

mark_safe

La valeur retournée par un filtre est sécurisée. Le signe < est converti par l'entité &lt; par exemple. Vous pouvez dire à Django que les données sont sûres et que le navigateur peut les interpréter:

backoffice/tempaltestags/perso.py

from django.safestring import mark_safe

def a(value, arg):
    return mark_safe('<a>%s</a>' % value)

L'organisation des templates

La plupart des projets Django utilisent le schéma de template suivant:

Une page qui sert de structure de base:

backoffice/templates/base.html

<!DOCTYPE html>
    <head>
        {% block head %}
        {% endblock head %}
    </head>
    <body>
        {% block header %}
        {% endblock header %}
        {% block content %}
        {% endblock content %}
        {% block menu %}
        {% endblock menu %}
        {% block footer %}
        {% endblock footer %}
    </body>
</html>

Et voici comment une page hérite de la structure de notre fichier base.html:

backoffice/templates/test.html

{% extends 'base.html' %}

{% block content %}
Je suis dans le content
{% endblock %}

Inclure un template dans un template

Il existe un template tag très utile pour inclure un template dans un autre:

{% include "subtemplate.html" %}

Vous pourrez ainsi alléger vos templates et améliorer leur lecture.

Créer des templates tags personnalisés

Vous pouvez bien évidemment créer vos propres templatetags personnalisés.

backoffice/templatetags/perso.py

from django import template
import datetime

register = template.Library()

class CurrentDateNode(template.Node):
    
    def __init__(self, format_string):

        self.format_string = format_string

    def render(self, context):
        
        return datetime.datetime.now().strftime(self.format_string)

@register.tag(name="current_date")
def current_date(parser, token):
    
    tag_name, format_string = token.split_contents()
    return CurrentDateNode(format_string[1:-1])

Notre template:

{% load perso %}

<p>Nous somme le: {% current_date "%d/%m/%Y" %}</p>