Un middleware c'est quoi?
Nous avons vu précédent comment l'objet request passe de la table de routage d' urls à la vue pour ensuite renvoyer une réponse au client. Ce principe est très simple d'utilisation, peut être même trop. Comment intercepter l' objet request avant qu'il n'atteigne les URL ou la vue? Et bien il existe une solution : les middlewares .
Le middleware est une classe qui contient 5 fonctions , chacune d'entres elles est appelée à un moment précis .
process_request (request) | est appelée à chaque requête, avant que Django décide quelle vue exécuter. Elle doit retourner un objet HttpResponse (ou None). Si elle retourne None, Django continuera à traiter la requête, exécutant tous les autres process_request() middleware puis process_view() et enfin la vue appropiée. Si elle retourne un objet HttpResponse, Django n'appellera aucune autre requête, vue ou Middleware exception. |
process_view (request, view_func, view_args, view_kwargs) | est appelée juste avant que Django appelle la vue. view_func est la fonction python que Django est sur le point d'utiliser. (Il s'agit de la fonction réelle et non de son nom). View_args est un dictionnaire des mots clé passé à la vue. Si la fonction process_view retourne un HttpResponse, aucune autre fonction middleware ne sera exécutée. |
process_template_response (request, response) | response est le TemplateResponse (ou équivalent) retournée par la vue Django ou par un middleware. process_template_response est appelée juste après que la vue ait été exécutée. Elle doit retourner une réponse objet contenant une méthode "render". |
process_response (request, response) | est appelée sur toutes les réponses avant qu'elles ne soit retournées au navigateur. C'est la function de la dernière étape du processus. |
process_exception (request, exception) | l'argument exception est un objet d'exception soulevée par la fonction de la vue. process_exception est appelée lorsqu'une vue soulève une exception, celle-ci doit retourner un objet réponse ou None. |
Django implémente de base plusieurs middlewares. Vous pouvez voir la liste des middlewares utilisés par Django dans la variable MIDDLEWARE_CLASSES du fichier de configuration de votre projet (settings.py)
La pratique
Bon assez de théorie, passons à la pratique. Voici comment créer un middleware de base:
eboutique/settings.py
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', [...], 'backoffice.middleware.ClassMiddleware' )
backoffice/middleware.py
class ClassMiddleware(object): # exécutée quand Django reçoit une requête et doit décider la vue à utiliser def process_request(self, request): pass # exécutée lorsque Django apelle la vue. On peut donc récupérer les arguments de la vue # view_func est la fonction Python que Django est sur le point d'utiliser. def process_view(self, request, view_func, view_args, view_kwargs): pass # executée lorsque la vue a levé une exception def process_exeption(self, request, exception): pass # La vue a été executée mais pas encore de compilation de template # ( il est encore possible de chager le template ) def process_template_response(self, request, response): return response # Tout est executée, dernier recours avant le retour client def process_response(self, request, response): return response
Exemple de temps de calcul d'une requête
Un exemple des plus connus est de calculer le temps nécessaire à une requête pour être exécutée.
backoffice/middleware.py
import datetime class ExecutionTimeMiddleWare(object): def process_request(self, request): pass def process_view(self, request, view_func, view_args, views_kwargs): request.time = datetime.datetime.now() def process_exeption(self, request, response, view_func, view_args, views_kwargs): pass def process_template_response(self, request, response): return response def process_response(self, request, response): if hasattr(request, 'time'): execution_time = datetime.datetime.now() - request.time else: execution_time = "Unknown" print "Execution time: %s secondes " % execution_time return response
Vous devriez voir le temps d'exécution de votre vue dans la console sous la forme suivante:
Execution time: 0:00:00.059770 secondes
Autre exemple de middleware
Nous souhaitons afficher dans notre terminal les requêtes SQL executées par la vue:
from django.conf import settings from django.db import connection from django.template import Template, Context class SQLMiddleWare(object): def process_response(self, request, response): if settings.DEBUG and connection.queries: execution_sql_time = sum([float(q['time']) for q in connection.queries]) t = Template( """ {{nb_sql}} requet{{nb_sql|pluralize:"e,es"}} en {{execution_sql_time}} second{{execution_sql_time|pluralize:"e,es"}}: {% for sql in sql_log %} [{{forloop.counter}}] {{sql.time}}s: {{sql.sql|safe}} {% endfor %} """) print "---------------" print t.render(Context({'sql_log':connection.queries,'nb_sql':len(connection.queries),'execution_sql_time':execution_sql_time})) print "---------------" return response
Une base de données par client
Les middleware permettent également de changer de base de données en fonction de paramètres:
eboutique/settings.py
DATABASES = { 'default' : { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'base1', 'USER': 'user1', 'PASSWORD': 'xxxxxx', 'HOST': '', 'PORT': '', }, 'customer1' : { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'customer1', 'USER': 'user2', 'PASSWORD': 'xxxxxx', } }
backoffice/middleware.py
from django.db import connection class SelectBaseMiddleWare(object): def process_request(self, request): if request.get_host() == "localhost:8001": cursor = connection.cursor() cursor.execute("USE base1; ") def process_response(self, request, response): if request.get_host() == "localhost:8001": cursor = connection.cursor() cursor.execute("USE base1; ") return response
conseil de lecture: Middlewares