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
# 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, 2019) 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:
2019-9-10
Les formats de date possibles:
b | Mois sur 3 lettres. exemple: 'jan' |
c | ISO 8601 format. exemple: 2019-01-02T10:30:00.000123+02:00 |
d | jour du mois sur 2 chiffres. exemple: '01' à '31' |
D | jour de la semaine sur 3 lettres. exemple: "Ven" |
e | Nom de la timezone |
E | Mois local |
f | heure. exemple: '12:45' |
F | Mois textuel. exemple: 'Janvier' |
g | Heure format 12H. exemple: '1' à '12' |
G | Heure format 24H. exemple: '0' à '23' |
h | Heure format 12H avec 0. exemple: '01' à '12' |
H | Heure format 24H avec 0. exemple: '01' à '23' |
i | Minutes avec 0. exemple: '00' à '59' |
j | Jour du mois sans 0. exemple: '1' à '31' |
l | Jour de la semaine textuel. exemple: 'Vendredi' |
L | Booléen pour savoir si c'est une année bissextile. |
m | Mois avec 0. exemple: '01' à '12' |
M | Mois sur 3 caractères. exemple 'JAN' |
n | Mois sans les 0. exemple: '1' à '12' |
N | Abbréviation du moins. exemple: 'Jan.', 'Fév.' |
O | Différence d'heure avec le temps Greenwich |
r | Format de date RFC 2822. exemple: 'Thu, 21 Dec 2019 16:01:07 +0200' |
s | Secondes avec 0. exemple: '00' à '59' |
t | Nombre de jours dans le mois. exemple: '28' à '31' |
u | Microsecondes. exemple: '0' à '999999' |
U | timestamp. Nombre de secondes écoulées depuis 01/01/1970 |
w | Jour de la semaine en chiffre. '0' pour dimanche et '6' pour samedi |
W | Numéro de la semaine. exemple: '1' à '53' |
y | Année sur deux chiffres. exemple: '14' |
Y | Année sur 4 chiffres. exemple: '2019' |
z | Jour 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é. |
date | format 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" |
dictsortreversed | idem que dictsort mais en sens inverse |
divisibleby | {{variable|divisibleby:"2"}} retourne True si la variable est divisble par "2" |
escape | convertit les caractères spéciaux en entité HTML |
escapejs | Convertit les caractères pour les utiliser dans des chaines de caractères javascript |
filesizeformat | Rend plus lisible une taille de fichier. Exemple: 123456789 affichera 117.7 MB |
first | Retourne 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" |
last | retourne le dernier item d'une liste |
length | Taille d'une liste/string |
length_is | {{variable|length_is:"2"}} retourne True si la taille de l'élément est 2 |
linebreaks | Remplace les retours à la ligne par la balise br. Chaque nouvelle ligne est encadrée par la balise p |
linebreaksbr | Remplace les retours à la ligne par la balise br. |
linenumbers | Chaque 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é < 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>