Context processor Django : ajouter automatiquement des variables aux contextes des templates

Pour constituer les pages rendues aux utilisateurs, nous avons régulièrement besoin de variables qui représentent des données génériques, non spécifiques à un contrôleur (view) particulier : items d'un menu général, derniers articles publiés, etc.

Rappels sur la notion de contexte

Lorsqu'un contrôleur (view) demande le rendu d'une vue (template), elle fournit à cette vue (template) un contexte. Le contexte est un ensemble de variables et de valeurs, qui pourront être utilisés dans la vue (template).

Fonctionnement du context processor de Django

Les processeurs de contexte sont exécutés après l'exécution du contrôleur (view), et permettent d'ajouter/modifier les variables de contexte. Un context processor est une simple fonction Python, qui se voit passer en paramètre un objet HttpRequest (comme les contrôleurs !) et doit retourner un dictionnaire (contenant les variables à ajouter au contexte). Une image vaut mieux qu'un long discours :

Context processor Django
Fonctionnement d'ensemble des processeurs de contexte Django

Paramétrage d'un context processor personnalisé

Pour utiliser nos processeurs de contexte personnalisés, nous devons les déclarer dans le fichier de configuration settings.py du projet, en utilisant la constante de configuration TEMPLATE_CONTEXT_PROCESSORS. Django vient avec des processeurs de contexte par défaut : il faut prendre soin de ne pas écraser le contenu de la constante TEMPLATE_CONTEXT_PROCESSORS, mais de simplement le compléter :

settings.py
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
    'context_processors.mon_processeur',
    'django.core.context_processors.request',
)

Remarquez que le code ci-dessus utilise un module nommé global_settings : il nous faut l'inclure à notre fichier settings.py :

settings.py
from django.conf import global_settings

Le reste du travail consiste à définir le processeur de contexte à proprement parler. Par convention, nous écrirons nos context processors dans un fichier nommé… context_processors.py, situé dans le répertoire de l'application qui le définit.

Exemple : un menu général dans le contexte !

Pour que les choses soient claires et simple, nous allons définir un processeur de contexte, concrètement.

Dans le cadre de notre application chistera, nous allons enrichir le contexte de tous nos templates en y ajoutant la liste 3 dernières user stories créées, afin de pouvoir les faire apparaître sur toutes les pages.

Pour cela, nous allons donc modifier notre fichier settings.py comme expliqué plus haut :

settings.py
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
    'context_processors.menu',
    'django.core.context_processors.request',
)

Puis, nous allons créer le fichier context_processor.py dans le répertoire de l'application chistera :

chistera/context_processors.py
from django.conf import settings
from chistera.models import UserStory

def menu(request):
    stories = UserStory.objects.all().order_by('-id')[:3]
    return {'last_stories': stories}

Comme vous le voyez, notre processeur de contexte est fort simple ! Il récupère les 3 dernières user stories créées (c'est à dire celles qui ont les plus grands id…), puis retourne un dictionnaire contenant une clé last_stories à qui on assigne la QuerySet retournée.

La dernière étape consiste à mettre à jour nos templates pour afficher ces stories sur toutes les pages. Bien entendu, et ceci grâce à la puissance su système de templating de Django, nous n'allons pas modifier tous nos templates, mais seulement le template de base qu'étendent tous les autres :

templates/base.html
<!DOCTYPE html>
<html>
    <head>
        <title>{% block title %}Miage Scrum{% endblock %}</title>
        <meta charset="utf-8">
    </head>

    <body>
        {% block body %}{% endblock %}

        <!-- Les modifications viennent ici : -->
        <hr />
        <h3>Dernières stories créées…</h3>
        <ul>
            {% for story in last_stories %}
                <li>{{ story.name }}</li>
            {% endfor %}
        </ul>
    </body>
</html>

Et voilà ! De la sorte, toutes nos vues (templates) qui étendent base.html présenteront, en bas de la page, la liste des 3 dernières user stories créées ! Nous utilisons pour cela la variable last_stories dont nous disposons à présent systématiquement dans le contexte grâce à notre context processor.

Context processor Django
La section « Dernières stories créées » est à présent disponible sur tous nos écrans !

Attention piège !

Si vous avez suivi ce tutoriel à la lettre à la suite du reste de la formation Django, vous rencontrez sans doute un problème : aucune erreur n'est générée, mais le contexte n'est pas complété, comme si le context processor était inactif. En fait… c'est le cas.

Pour que les context processors personnalisés fonctionnent, vous devez modifier légèrement la dernière ligne de vos contrôleurs (views), lors de l'appel de la fonction render_to_response.

Modifiez donc vos anciens retours simples :

chistera/views.html
#...
return render_to_response('chistera/dashboard.html', {'backlogs': backlogs, 'teams': teams})

Par des retours mentionnant spécifiant de manière explicite un paramètre context_instance, dont la valeur est calculée par RequestContext :

chistera/views.html
#...
return render_to_response('chistera/dashboard.html', {'backlogs': backlogs, 'teams': teams}, context_instance=RequestContext(request))

Notez qu'il vous faudra pour cela, comme d'habitude, inclure cet outil dans votre fichier views.py :

chistera/views.html
from django.template import RequestContext

Et le tour est joué !

Dans la suite de ce cours Django et dans vos développements futurs, je vous conseille de faire mention, systématiquement, de ce paramètre context_instance=RequestContext(request) lors de l'appel de render_to_response() en retour de contrôleurs (views).

Testez vos connaisances…

Quand agit un processeur de contexte ?
  • Après que le contrôleur (view) ait été exécuté et avant le rendu de la vue (template).
  • Avant l'exécution du contrôleur (view).
  • Après le rendu de la vue (template).
  • Seulement quand on en fait la demande.
Quand a-t-on intérêt d'utiliser des processeurs de contexte ?
  • Quand plusieurs contrôleurs (views) doivent placer les mêmes choses dans le contexte.
  • Jamais : ce n'est pas obligatoire et c'est superflu.
  • Quand le nombre de contrôleurs commence à être trop important.
  • Quand plusieurs vuew (templates) ont besoin des mêmes variables de contexte pour être rendus correctement.
  • Quand on souhaite créer une class based view.

Soient le processeur de contexte, le contrôleur et le template suivants. Que sera affiché à l'écran après rendu du template ?

# context_processors.py
def my_wonderful_context_processor(request):
    names = ["Luke Duke", "Bo Duke", "Daisy Duke", "Rosco P. Coltrane", "Jefferson Davis « Boss » Hogg"]
    return {'person_names': names}
# views.py 
def oh_the_view(request):
    names = ["John Smith", "Templeton Peck", "Henry Murdock", "Bosco Albert Baracus"]
    return render_to_response('my_app/people.html', {'person_names': names}, context_instance=RequestContext(request))
# my_app/people.html 
{% for name in person_names %}
    <li>{{ name }}</li>
{% endfor %}
  • John Smith
    Templeton Peck
    Henry Murdock
    Bosco Albert Baracus
  • John Smith
    Templeton Peck
    Henry Murdock
    Bosco Albert Baracus
    Luke Duke
    Bo Duke
    Daisy Duke
    Rosco P. Coltrane
    Jefferson Davis Hogg
  • Luke Duke
    Bo Duke
    Daisy Duke
    Rosco P. Coltrane
    Jefferson Davis Hogg
  • Luke Duke
    Bo Duke
    Daisy Duke
    Rosco P. Coltrane
    Jefferson Davis Hogg
    John Smith
    Templeton Peck
    Henry Murdock
    Bosco Albert Baracus