Django et les Template Context Processors

S’il y a bien une question que tout développeur se pose, c’est bien « Comment vais je éviter la redondance de code ? ». Après de longues recherches, dans un résultat perdu au milieu des pages Google, j’ai trouvé ce vieil article (datant de 2006 quand même). Grâce à cet article, j’ai appris qu’il faut se servir des Template Context Processors. Oui, mais comment ? C’est ce que nous allons voir dans la suite de l’article.

Les Template Context Processors

Les Template Context Processors ont pour but de peupler de données notre RequestContext. Il s’agit d’un tuple contenant une liste de fonctions qui retournent les valeurs dont on a besoin ensuite dans nos templates sous forme d’un dictionnaire. Elles prennent en paramètre un objet request.

Afin de mieux visualiser, nous verrons avec un exemple. Je souhaite afficher la date sur toutes les pages (dans un fichier base.html, page squelette de notre template), nous aurons un template qui ressemble à :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
  <head>
    <title>{% block title %}Generic Page{% endblock %}</title>
  </head>
  <body>
    <div id="header">
      {{current_date}}
    </div>
    <div id="content">
      {% block content %}Mon content{% endblock %}
    </div>
  </body>
</html>

Nous avons de plus 2 fichiers templates : un_template.html et un_autre_template.html qui héritent de base.html.
Et 2 vues qui ressemblent à :

1
2
3
4
5
6
7
8
9
10
from django.shortcuts import render_to_response
import datetime
 
def ma_vue(request):
    now = datetime.datetime.now()
    return render_to_response('un_template.html', {'current_date' : now }) 
 
def mon_autre_vue(request):
    now = datetime.datetime.now()
    return render_to_response('un_autre_template.html', {'current_date' : now })

Si nous voulons afficher l’heure sur toutes les pages, il nous faudra à chaque fois dans notre vue retourner dans notre dictionnaire la valeur de current_date. Long, répétitif… bref, pas du tout adapté. Modifions donc notre code pour rendre cela moins redondant.

Pour commencer, il nous faut créer un fichier, que nous appellerons context_processors.py. Ce fichier contiendra une fonction qui retournera un dictionnaire avec notre date. Le voici :

1
2
3
4
5
import datetime
 
def affiche_date(request):
    now = datetime.datetime.now()
    return {'current_date' : now }

Il défini la fonction affiche_date, qui récupère la date du jour, et le retourne dans un dictionnaire qui possède une seule clé : current_date.

Il faut modifier nos vues en conséquence.

1
2
3
4
5
6
7
8
9
10
from django.shortcuts import render_to_response
from django.template import RequestContext
 
def ma_vue(request):
    return render_to_response('un_template.html', 
                           context_instance=RequestContext(request)) 
 
def mon_autre_vue(request):
    return render_to_response('un_autre_template.html', 
                           context_instance=RequestContext(request))

render_to_response utilise par défaut une instance de Context pour le rendu. Cependant nous utilisons les Template Context Processors, il est impératif d’utiliser une instance RequestContext à la place, d’où le context_instance=RequestContext(request).

Maintenant que nous avons modifié nos fonctions, il faut spécifier à Django la méthode qui retourne notre current_date dans notre instance de RequestContext. Pour cela, modifions le fichier settings.py, et cherchez l’option TEMPLATE_CONTEXT_PROCESSORS . Par défaut, vous devriez avoir ces valeurs :

1
2
3
4
5
6
7
TEMPLATE_CONTEXT_PROCESSORS = (
   "django.contrib.auth.context_processors.auth",
   "django.core.context_processors.debug",
   "django.core.context_processors.i18n",
   "django.core.context_processors.media",
   "django.contrib.messages.context_processors.messages"
)

Si ce n’est pas le cas, rajoutez les. Rajoutons maintenant notre fonction qui retourne la date du jour à la fin de notre tuple comme ceci :

1
2
3
4
5
6
7
8
TEMPLATE_CONTEXT_PROCESSORS = (
   "django.contrib.auth.context_processors.auth",
   "django.core.context_processors.debug",
   "django.core.context_processors.i18n",
   "django.core.context_processors.media",
   "django.contrib.messages.context_processors.messages",
   "monsite.context_processors.affiche_date"
)

Et c’est fini. Maintenant, current_date sera disponible dans tous nos templates.

Le cas des vues génériques

Dans le cas où vous utilisez des vues génériques, pas beaucoup de modifications à réaliser. En effet, celles-ci utilisent par défaut RequestContext.

Améliorons encore un peu…

En naviguant, j’ai trouvé qu’on pouvait encore réduire notre code en ajoutant ce code tiré de djangosnippets :

1
2
3
4
5
6
7
from django.shortcuts import render_to_response
from django.template import RequestContext
 
def render_response(req, *args, **kwargs):
    kwargs['context_instance'] = RequestContext(req)
    return render_to_response(*args, **kwargs)
)

Cela réduit encore la redondance dans nos vues.