Ce document présente le système de gabarits de Django dans une perspective technique : son fonctionnement et ses possibilités d’extension. Si vous recherchez simplement la référence de la syntaxe du langage, consultez Le langage de gabarit de Django.
Si vous évaluez la possibilité d’utiliser le système de gabarits de Django à partir d’une autre application, sans le reste du système, prenez soin de lire la section configuration plus bas dans ce document.
Un gabarit est un document texte ou un chaîne Python normale, qui est balisée à l’aide du langage de gabarit de Django. Un gabarit peut contenir des balises de bloc ou des variables.
Une balise de bloc est un symbole dans un gabarit qui fait quelque chose.
Cette définition est volontairement vague. Par exemple, une balise de bloc peut produire du contenu, servir de structure de contrôle (une instruction « if » ou une boucle « for »), extraire du contenu d’une base de données ou donner accès à d’autres balises de gabarit.
Les balises de bloc sont entourées par "{%" et "%}".
Exemple de gabarit contenant des balises de bloc :
{% if is_logged_in %}Thanks for logging in!{% else %}Please log in.{% endif %}
Une variable est un symbole dans un gabarit qui affiche une valeur.
Les balises de variables sont entourées par "{{" et "}}".
Exemple de gabarit avec des variables :
My first name is {{ first_name }}. My last name is {{ last_name }}.
Un contexte est un ensemble de correspondances « nom de variable » -> « valeur de variable » transmis à un gabarit.
Un gabarit effectue le rendu d’un contexte en remplaçant les « trous » des variables par des valeurs tirées du contexte et en exécutant toutes les balises de bloc.
L’utilisation du système de gabarits en Python est un processus à deux étapes :
Premièrement, il faut compiler le code brut du gabarit en un objet Template.
Puis, il faut appeler la méthode render() de l’objet Template avec un contexte donné.
La façon la plus simple de créer un objet Template est d’en instancier un directement. La classe se trouve dans django.template.Template. Le constructeur accepte un paramètre, le code brut du gabarit :
>>> from django.template import Template
>>> t = Template("My name is {{ my_name }}.")
>>> print(t)
<django.template.Template instance>
En coulisses
Le système n’analyse qu’une seule fois le code brut du gabarit, au moment de la création de l’objet Template. Par la suite, le résultat est stocké en interne sous forme de structure de « nœuds » pour des raisons de performance.
Même l’analyse en soi est assez rapide. La plupart de l’analyse se déroule dans un seul appel à une seule et courte expression régulière.
À partir du moment où vous avez un objet Template compilé, vous pouvez procéder au rendu d’un contexte ou de plusieurs contextes. La classe Context se trouve dans django.template.Context, et son constructeur accepte deux paramètres (facultatifs) :
Un dictionnaire faisant correspondre des noms de variables à des valeurs de variables.
Le nom de l’application en cours. Ce nom d’application est utilisé pour aider à la résolution des URL avec espace de noms. Si vous n’utilisez pas d’URL avec espace de noms, vous pouvez ignorer ce paramètre.
Appelez la méthode render() de l’objet Template avec le contexte qui doit « remplir » le gabarit :
>>> from django.template import Context, Template
>>> t = Template("My name is {{ my_name }}.")
>>> c = Context({"my_name": "Adrian"})
>>> t.render(c)
"My name is Adrian."
>>> c = Context({"my_name": "Dolores"})
>>> t.render(c)
"My name is Dolores."
Les noms de variables peuvent contenir des lettres (A-Z), des chiffres (0-9), des soulignements et des points (mais elles ne peuvent pas commencer par un soulignement).
Les points ont une signification particulière dans le rendu des gabarits. Un point dans un nom de variable indique l’accès à un sous-élément. Plus précisément, lorsque le système de gabarit voit un point dans un nom de variable, il recherche des sous-éléments dans cet ordre :
Accès dictionnaire. Exemple : foo["bar"]
Accès attribut. Exemple : foo.bar
Accès par index de liste. Exemple : foo[bar]
Notez que « bar » dans une expression de gabarit comme {{ foo.bar }} est interprété comme une chaîne littérale et même si une variable « bar » existe dans le contexte du gabarit, elle ne sera pas appelée.
Le système de gabarits utilise le premier accès qui fonctionne. C’est une logique de court-circuit. Voici quelques exemples :
>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."
>>> class PersonClass: pass
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."
>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."
Si un élément de la variable est un objet exécutable, le système de gabarits essaye de l’appeler. Exemple :
>>> class PersonClass2:
... def name(self):
... return "Samantha"
>>> t = Template("My name is {{ person.name }}.")
>>> t.render(Context({"person": PersonClass2}))
"My name is Samantha."
Les variables exécutables sont légèrement plus complexes que les variables qui ne demandent que des accès directs. Voici quelques éléments à garder en tête :
If the variable raises an exception when called, the exception will be propagated, unless the exception has an attribute silent_variable_failure whose value is True. If the exception does have a silent_variable_failure attribute whose value is True, the variable will render as the value of the TEMPLATE_STRING_IF_INVALID setting (an empty string, by default). Example:
>>> t = Template("My name is {{ person.first_name }}.")
>>> class PersonClass3:
... def first_name(self):
... raise AssertionError("foo")
>>> p = PersonClass3()
>>> t.render(Context({"person": p}))
Traceback (most recent call last):
...
AssertionError: foo
>>> class SilentAssertionError(Exception):
... silent_variable_failure = True
>>> class PersonClass4:
... def first_name(self):
... raise SilentAssertionError
>>> p = PersonClass4()
>>> t.render(Context({"person": p}))
"My name is ."
Notez que django.core.exceptions.ObjectDoesNotExist, qui est la classe de base de toutes les exceptions DoesNotExist de l’API de base de données de Django, contient silent_variable_failure = True. Ainsi, si vous utilisez les gabarits de Django avec des objets modèles de Django, toute exception DoesNotExist échoue silencieusement.
A variable can only be called if it has no required arguments. Otherwise, the system will return the value of TEMPLATE_STRING_IF_INVALID.
Il est clair qu’il peut exister des effets de bord lors de l’appel à certaines variables et il serait insensé ou sécuritairement désastreux de permettre au système de gabarits d’y accéder.
Un bon exemple est la méthode delete() de chaque objet modèle de Django. Le système de gabarits ne devrait pas permettre de faire quelque chose comme :
I will now delete this valuable data. {{ data.delete }}
Pour empêcher cela, définissez un attribut alters_data sur la variable exécutable. Le système de gabarits n’appellera pas une variable si elle contient alters_data=True et la remplace plutôt par le contenu de TEMPLATE_STRING_IF_INVALID, sans conditions. Les méthodes générées dynamiquement delete() et save() sur les objets modèles de Django reçoivent automatiquement alters_data=True. Exemple :
def sensitive_function(self):
self.database_record.delete()
sensitive_function.alters_data = True
Vous pouvez parfois avoir envie de désactiver cette fonctionnalité pour d’autres raisons, et dire au système de gabarits de ne pas interpréter une variable, peu importe le contexte. Pour ce faire, il faut définir un attribut do_not_call_in_templates sur la variable exécutable, avec la valeur True. Le système de gabarits va alors considérer que la variable ne peut pas être appelée (ce qui permet par exemple d’accéder aux attributs de l’objet exécutable).
Généralement, si une variable n’existe pas, le système des gabarits insère la valeur du réglage TEMPLATE_STRING_IF_INVALID, qui vaut '' (chaîne vide) par défaut.
Les filtres appliqués à une variable non valide ne seront appliqués que si TEMPLATE_STRING_IF_INVALID est défini à '' (la chaîne vide). Si TEMPLATE_STRING_IF_INVALID est défini à une autre valeur, les filtres de variable seront ignorés.
Ce comportement est légèrement différent pour les balises de gabarit if, for et regroup. Si une variable non valide est fournie à l’une de ces balises de gabarit, la variable sera interprétée comme None. Les filtres sont toujours appliqués aux variables non valides dans ces balises de gabarit.
Si TEMPLATE_STRING_IF_INVALID contient un substituant '%s', ce dernier sera remplacé par le nom de la variable non valide.
À des fins de débogage uniquement !
Bien que TEMPLATE_STRING_IF_INVALID puisse être un outil de débogage utile, il n’est pas conseillé de l’activer de manière permanente durant le développement.
Beaucoup de gabarits, y compris ceux du site d’administration, comptent sur le comportement silencieux du système de gabarit lorsqu’une variable non existante apparaît. Si TEMPLATE_STRING_IF_INVALID contient une valeur autre que '', vous allez rencontrer des problèmes d’affichage avec ces gabarits et ces sites.
Généralement, TEMPLATE_STRING_IF_INVALID ne devrait être activé que pour déboguer un problème spécifique d’un gabarit, puis réinitialisé une fois cette phase terminée.
Tous les contextes contiennent True, False et None. Comme on peut s’y attendre, ces variables correspondent aux objets Python équivalents.
Avant Django 1.5, ces variables n’étaient pas considérées comme des cas spéciaux et étaient évaluées à None, sauf si elles étaient définies dans le contexte.
La plupart du temps, les objets Context sont créés en transmettant à Context() un dictionnaire contenant les données utiles. Mais il est toujours possible d’ajouter ou d’enlever des éléments d’un objet Context après sa création, en employant la syntaxe habituelle des dictionnaires :
>>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
''
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'
Un objet Context est une pile. C’est-à-dire que vous pouvez lui appliquer les méthodes push() et pop(). En cas de pop() en trop, une exception django.template.ContextPopException est générée :
>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.push()
{}
>>> c['foo'] = 'second level'
>>> c['foo']
'second level'
>>> c.pop()
{'foo': 'second level'}
>>> c['foo']
'first level'
>>> c['foo'] = 'overwritten'
>>> c['foo']
'overwritten'
>>> c.pop()
Traceback (most recent call last):
...
django.template.ContextPopException
En plus de push() et pop(), l’objet Context définit également une méthode update(). Elle fonctionne comme push(), mais accepte en paramètre un dictionnaire et place ce dictionnaire sur la pile, au lieu d’un dictionnaire vide.
>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.update({'foo': 'updated'})
{'foo': 'updated'}
>>> c['foo']
'updated'
>>> c.pop()
{'foo': 'updated'}
>>> c['foo']
'first level'
L’utilisation de Context comme une pile est bien pratique avec certaines balises de gabarit personnalisées, comme vous le verrez plus loin.
Django contient une classe Context spéciale, django.template.RequestContext, qui se comporte légèrement différemment de l’objet django.template.Context normal. La première différence est qu’elle demande un objet HttpRequest comme premier paramètre. Par exemple :
c = RequestContext(request, {
'foo': 'bar',
})
La seconde différence est qu’elle remplit automatiquement le contexte avec quelques variables, en fonction du réglage TEMPLATE_CONTEXT_PROCESSORS.
Le réglage TEMPLATE_CONTEXT_PROCESSORS est un tuple d’objets exécutables appelés processeurs de contexte qui acceptent un objet requête en paramètre et renvoient un dictionnaire d’éléments à fusionner dans le contexte. Par défaut, TEMPLATE_CONTEXT_PROCESSORS contient :
("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.core.context_processors.tz",
"django.contrib.messages.context_processors.messages")
En plus de ce contenu, RequestContext utilise toujours django.core.context_processors.csrf. Il s’agit d’un processeur de contexte lié à la sécurité exigé par l’application d’administration ainsi que d’autres applications contribuées. Il est volontairement ajouté de force pour qu’il ne puisse pas être enlevé par une erreur de configuration du réglage TEMPLATE_CONTEXT_PROCESSORS.
Chaque processeur est appliqué successivement. Cela signifie que si un processeur ajoute une variable au contexte et que le processeur suivant ajoute une variable de même nom, la seconde écrase la première. Les processeurs par défaut sont présentés ci-dessous.
Quand les processeurs de contexte sont-ils appliqués ?
Les processeurs de contexte sont appliqués après le traitement du contexte lui-même. Cela signifie qu’un processeur de contexte peut écraser une variable que vous avez fournie dans un objet Context ou RequestContext, il faut donc faire attention de ne pas utiliser des variables dont le nom peut entrer en conflit avec ceux des processeurs de contexte installés.
Il est également possible de donner à RequestContext une liste de processeurs supplémentaires en utilisant le troisième paramètre positionnel facultatif, processors. Dans cet exemple, l’instance RequestContext reçoit une variable ip_address:
from django.http import HttpResponse
from django.template import RequestContext
def ip_address_processor(request):
return {'ip_address': request.META['REMOTE_ADDR']}
def some_view(request):
# ...
c = RequestContext(request, {
'foo': 'bar',
}, [ip_address_processor])
return HttpResponse(t.render(c))
Note
If you’re using Django’s render_to_response() shortcut to populate a template with the contents of a dictionary, your template will be passed a Context instance by default (not a RequestContext). To use a RequestContext in your template rendering, use the render() shortcut which is the same as a call to render_to_response() with a context_instance argument that forces the use of a RequestContext.
Voici ce que font chacun des processeurs de contexte par défaut :
Si TEMPLATE_CONTEXT_PROCESSORS contient ce processeur, chaque RequestContext contiendra ces variables :
user – une instance auth.User représentant l’utilisateur actuellement connecté (ou une instance AnonymousUser, si le client n’est pas connecté).
perms – une instance de django.contrib.auth.context_processors.PermWrapper, représentant les permissions de l’utilisateur actuellement connecté.
Si TEMPLATE_CONTEXT_PROCESSORS contient ce processeur, chaque RequestContext contient ces deux variables – mais seulement si le réglage DEBUG vaut True et que l’adresse IP de la requête (request.META['REMOTE_ADDR']) se trouve dans le réglage INTERNAL_IPS:
debug – True. Vous pouvez l’utiliser dans les gabarits pour tester si vous êtes en mode DEBUG.
sql_queries – une liste de dictionnaires {'sql': ..., 'time': ...} représentant chaque requête SQL effectuée jusque-là dans le traitement de la requête et le temps nécessaire à son exécution. La liste est triée dans l’ordre des requêtes.
Si TEMPLATE_CONTEXT_PROCESSORS contient ce processeur, chaque RequestContext contiendra ces deux variables :
LANGUAGES – la valeur du réglage LANGUAGES.
LANGUAGE_CODE – request.LANGUAGE_CODE, si elle existe. Sinon, la valeur du réglage LANGUAGE_CODE.
Voir Internationalisation et régionalisation pour plus de détails.
Si TEMPLATE_CONTEXT_PROCESSORS contient ce processeur, chaque RequestContext contiendra une variable MEDIA_URL, équivalente au réglage MEDIA_URL.
Si TEMPLATE_CONTEXT_PROCESSORS contient ce processeur, chaque RequestContext contiendra une variable STATIC_URL, équivalente au réglage STATIC_URL.
Ce processeur ajoute un jeton requis par la balise de gabarit csrf_token pour se protéger des attaques de type Cross Site Request Forgeries.
Si TEMPLATE_CONTEXT_PROCESSORS contient ce processeur, chaque RequestContext contiendra une variable request correspondant à l’objet HttpRequest actuel. Notez que ce processeur n’est pas actif par défaut ; il est nécessaire de l’activer soi-même.
Si TEMPLATE_CONTEXT_PROCESSORS contient ce processeur, chaque RequestContext contiendra cette variable supplémentaire :
messages – une liste de messages (sous forme de chaînes) qui ont été définis au travers de l’infrastructure des messages.
Un processeur de contexte possède une interface très simple : ce n’est qu’une fonction Python acceptant un paramètre, un objet HttpRequest, et renvoyant un dictionnaire qui est ensuite ajouté au contexte de gabarit. Chaque processeur de contexte doit renvoyer un dictionnaire.
Les processeurs de contexte personnalisés peuvent se trouver n’importe où dans le code. Tout ce que Django demande, c’est que le réglage TEMPLATE_CONTEXT_PROCESSORS contienne le chemin vers le processeur personnalisé.
En général, il est conseillé de stocker les gabarits sur le système de fichiers plutôt que de faire appel à l’API de gabarit de bas niveau. Les gabarits doivent se trouver dans un répertoire désigné comme répertoire de gabarits.
Django recherche les répertoires de gabarits à plusieurs endroits, en fonction du réglage du chargeur de gabarits (voir « Types de chargeurs » ci-dessous), mais la façon la plus élémentaire de désigner des répertoires de gabarits est d’utiliser le réglage TEMPLATE_DIRS.
Indiquez à Django quels sont vos répertoires de gabarits en utilisant le réglage TEMPLATE_DIRS dans votre fichier de réglages. Il devrait contenir une liste ou un tuple de chaînes contenant les chemins complets vers le ou les répertoires de gabarits. Exemple :
TEMPLATE_DIRS = (
"/home/html/templates/lawrence.com",
"/home/html/templates/default",
)
Les gabarits peuvent se trouver n’importe où, pour autant que leur emplacement soit lisible par le serveur Web. Leur extension est également à votre bon plaisir, .html, .txt, ou même sans extension du tout.
Notez que ces chemins doivent utiliser les barres obliques de style Unix, même avec Windows.
django.template.loader possède deux fonctions pour charger des gabarits à partir de fichiers :
get_template renvoie le gabarit compilé (un objet Template) correspondant au gabarit du nom indiqué. Si ce gabarit n’existe pas, une exception django.template.TemplateDoesNotExist est générée.
select_template est semblable à get_template, sauf qu’il accepte une liste de noms de gabarits. Il renvoie le premier gabarit existant de la liste.
Par exemple, si vous appelez get_template('story_detail.html') avec le réglage TEMPLATE_DIRS comme ci-dessus, voici les fichiers que Django va rechercher, dans l’ordre :
Si vous appelez select_template(['story_253_detail.html', 'story_detail.html']), voici ce que Django va rechercher :
Lorsque Django trouve un gabarit existant, il stoppe sa recherche.
Astuce
Vous pouvez utiliser select_template() pour une souplesse maximale de sélection des gabarits. Par exemple, si vous avez rédigé un article et que vous voulez pouvoir utiliser des gabarits spécifiques pour certains articles, utilisez quelque chose comme select_template(['story_%s_detail.html' % story.id, 'story_detail.html']). Cela vous permet d’utiliser un gabarit adapté à un article individuel, tout en se rabattant sur un gabarit standard pour les articles sans gabarit dédié.
Il est possible – et préférable – d’organiser les gabarits dans des sous-répertoires du répertoire des gabarits. La convention est de créer un sous-répertoire par application Django, en y ajoutant d’autres sous-répertoires au besoin.
Ce conseil est tout à votre avantage. Le placement de tous les gabarits au niveau racine d’un seul répertoire devient rapidement ingérable.
Pour charger un gabarit se trouvant dans un sous-répertoire, il suffit d’utiliser la barre oblique, comme ceci :
get_template('news/story_detail.html')
Toujours avec le même réglage TEMPLATE_DIRS que ci-dessus, cet exemple d’appel get_template() va tenter de charger les gabarits suivants :
Par défaut, Django utilise un chargeur de gabarit basé sur le système de fichiers, mais il existe également quelques autres chargeurs de gabarit, qui savent comment charger des gabarits à partir d’autres sources.
Certains de ces autres chargeurs sont désactivés par défaut, mais vous pouvez les activer en modifiant le réglage TEMPLATE_LOADERS. TEMPLATE_LOADERS doit contenir un tuple de chaînes, où chaque chaîne représente une classe de chargeur de gabarit. Voici les chargeurs de gabarit que Django propose :
django.template.loaders.filesystem.Loader
Charge les gabarits à partir du système de fichiers, en fonction de TEMPLATE_DIRS. Ce chargeur est activé par défaut.
django.template.loaders.app_directories.Loader
Charge les gabarits à partir des dossiers d’applications Django sur le système de fichiers. Pour chaque application dans INSTALLED_APPS, le chargeur cherche un sous-répertoire nommé templates. S’il le trouve, Django recherche les gabarits dans ce répertoire.
Cela signifie que vous pouvez stocker les gabarits à l’intérieur des différentes applications. Cela facilite également la distribution des applications Django dotées de gabarits par défaut.
Par exemple, avec ce réglage :
INSTALLED_APPS = ('myproject.polls', 'myproject.music')
…get_template('foo.html') recherche foo.html dans ces répertoires, dans l’ordre :
/chemin/vers/myproject/polls/templates/
/chemin/vers/myproject/music/templates/
… et utilise le premier qu’il trouve.
L’ordre de INSTALLED_APPS est important ! Par exemple, si vous souhaitez personnaliser l’administration de Django, il peut être utile de surcharger le gabarit admin/base_site.html standard de django.contrib.admin par votre propre admin/base_site.html dans myproject.polls. Vous devez alors vous assurer que myproject.polls apparaisse avant django.contrib.admin dans INSTALLED_APPS, sinon django.contrib.admin sera chargé en premier et votre gabarit sera ignoré.
Notez que le chargeur effectue une optimisation lors de sa première importation : il place en cache une liste des paquets INSTALLED_APPS qui possèdent un sous-répertoire templates.
Ce chargeur est activé par défaut.
django.template.loaders.eggs.Loader
Semblable à app_directories ci-dessus, mais charge les gabarits à partir d’objets eggs Python au lieu du système de fichiers.
Ce chargeur est désactivé par défaut.
django.template.loaders.cached.Loader
Par défaut, le système des gabarits lit et compile les gabarits chaque fois qu’ils doivent être produits. Même si le système des gabarits de Django est assez rapide, la charge de lecture et de compilation des gabarits peut être conséquente.
Le chargeur de gabarits « en cache » est un chargeur basé sur une classe qu’il est possible de configurer en lui donnant une liste d’autres chargeurs qu’il va encapsuler. Les chargeurs encapsulés sont utilisés pour retrouver les gabarits inconnus la première fois qu’ils sont référencés. Le chargeur « en cache » stocke ensuite l’objet Template compilé en mémoire. Cette instance mise en cache est renvoyée lors de chaque nouvelle requête de chargement de ce même gabarit.
Par exemple, pour activer la mise en cache des gabarits avec les chargeurs de gabarit filesystem et app_directories, voici le réglage qu’il faudrait utiliser :
TEMPLATE_LOADERS = (
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
)
Note
Toutes les balises de gabarit intégrées à Django peuvent être utilisées sans problème avec le chargeur « en cache », mais si vous utilisez des balises de gabarit personnalisées provenant de paquets externes ou que vous avez écrites vous-même, il est nécessaire de vérifier que l’implémentation des objets Node de chaque balise respecte la concurrence (« thread-safe »). Pour plus d’informations, consultez les indications sur la concurrence entre fils d’exécution dans les balises de gabarit.
Ce chargeur est désactivé par défaut.
Django utilise les chargeurs de gabarit dans l’ordre de leur apparition dans le réglage TEMPLATE_LOADERS. Il utilise chaque chargeur jusqu’à ce qu’il trouve une correspondance.
Pour rationaliser l’aspect répétitif du chargement et de la production des gabarits, Django propose une fonction raccourci qui automatise le processus : render_to_string() dans django.template.loader, qui charge un gabarit, le produit et renvoie la chaîne textuelle résultante :
from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', {'foo': 'bar'})
Le raccourci render_to_string accepte un paramètre obligatoire, template_name, qui doit correspondre au nom du gabarit à charger et à produire (ou une liste de noms de gabarits, auquel cas Django utilise le premier gabarit de la liste qui existe), et deux paramètres facultatifs :
Un dictionnaire dont le contenu servira de variables et de valeurs pour le contexte du gabarit. Il peut également être transmis comme deuxième paramètre positionnel.
Une instance de Context ou une sous-classe (par ex. une instance de RequestContext) à utiliser comme contexte de gabarit. Cela peut aussi être transmis comme troisième paramètre facultatif.
Voir aussi le raccourci render_to_response() qui appelle render_to_string et fournit le résultat à un objet HttpResponse prêt à être directement renvoyé depuis une vue.
Note
Cette section ne concerne que les personnes qui souhaitent utiliser le système de gabarit comme composant de sortie depuis une autre application. Si vous utilisez le système de gabarit dans le cadre d’une application Django, rien de ce qui suit ne vous concerne.
Normalement, Django charge toutes les informations de configuration dont il a besoin à partir de son propre fichier de configuration par défaut, combinées avec les réglages du module désigné par la variable d’environnement DJANGO_SETTINGS_MODULE. Mais si vous utilisez le système de gabarit de manière indépendante du reste de Django, l’approche par variable d’environnement n’est pas très pratique, parce que vous allez probablement vouloir configurer le système de gabarit en accord avec votre application plutôt que d’avoir affaire à des fichiers de réglages et devoir les désigner par le moyen de variables d’environnement.
Pour résoudre ce problème, vous devez employez l’option de configuration manuelle décrite dans Utilisation des réglages sans définir DJANGO_SETTINGS_MODULE. Il suffit alors d’importer les éléments adéquats du système de gabarit puis, avant d’appeler la première fonction de gabarit, appeler django.conf.settings.configure() avec d’éventuels réglages que vous souhaitez voir appliquer. Dans ces réglages, il faut au moins considérer TEMPLATE_DIRS (si vous prévoyez d’utiliser les chargeurs de gabarits), DEFAULT_CHARSET (bien que la valeur par défaut utf-8 est probablement suffisante) et TEMPLATE_DEBUG. Si vous pensez utiliser la balise de gabarit url, il faudra aussi définir le réglage ROOT_URLCONF. Tous les réglages disponibles sont décrits dans la documentation des réglages, et tout réglage commençant par TEMPLATE_ vaut la peine d’être pris en compte.
Les classes Template et Loader de Django implémentent une API simple pour le chargement et la production de gabarits. En fournissant quelques classes d’adaptation simples implémentant cette API, il est possible d’utiliser des systèmes de gabarit externes comme Jinja2 ou Cheetah. Cela permet d’utiliser des bibliothèques de gabarit externes sans devoir abandonner des fonctionnalités Django utiles comme l’objet Context ou des raccourcis pratiques comme render_to_response().
Le composant au cœur du système de gabarit de Django est la classe Template. Cette classe présente une interface très simple : son constructeur accepte une seul paramètre positionnel désignant la chaîne de gabarit et d’autre part, elle possède une méthode render() acceptant un objet Context et renvoyant une chaîne contenant la réponse produite.
Supposons que l’on utilise un langage de gabarit définissant un objet Template avec une méthode render() acceptant un dictionnaire plutôt qu’un objet Context. Nous pouvons écrire un adaptateur simple implémentant l’interface Template de Django :
import some_template_language
class Template(some_template_language.Template):
def render(self, context):
# flatten the Django Context into a single dictionary.
context_dict = {}
for d in context.dicts:
context_dict.update(d)
return super(Template, self).render(context_dict)
C’est tout ce qui est nécessaire pour rendre notre classe de démonstration Template compatible avec le système de chargement et de production de Django !
L’étape suivante est d’écrire une classe Loader qui renvoie des instances de notre propre classe de gabarit au lieu de la classe Template par défaut. Les classes Loader personnalisées doivent hériter de django.template.loader.BaseLoader et surcharger la méthode load_template_source() qui accepte un paramètre template_name, charge le gabarit correspondant à partir du disque (ou d’ailleurs) et renvoie un tuple : (template_string, template_origin).
La méthode load_template() de la classe Loader récupère la chaîne de gabarit en appelant load_template_source(), crée une instance de Template à partir de cette source et renvoie un tuple : (template, template_origin). Comme il s’agit de la méthode qui crée réellement l’instance de Template, nous devons la surcharger dans notre classe de gabarit personnalisée. Nous pouvons hériter de la classe intégrée django.template.loaders.app_directories.Loader pour profiter de la méthode load_template_source() qui s’y trouve :
from django.template.loaders import app_directories
class Loader(app_directories.Loader):
is_usable = True
def load_template(self, template_name, template_dirs=None):
source, origin = self.load_template_source(template_name, template_dirs)
template = Template(source)
return template, origin
Pour terminer, nous devons modifier nos réglages de projet, indiquant à Django d’utiliser notre chargeur personnalisé. Maintenant nous pouvons écrire tous nos gabarits dans le langage de gabarit alternatif tout en continuant à utiliser le reste du système de gabarit de Django.
Jan 13, 2016