Django by Example: Middleware Request Hooks
Middleware is a framework of hooks into Django's request/response processing. This sample code demonstrates a light, low-level "plugin" system for globally altering Django's input or output.
Code
import time
class SimpleTimingMiddleware:
def __init__(self, get_response):
# One-time configuration and initialization.
self.get_response = get_response
def __call__(self, request):
# 1. Code to be executed for each request BEFORE
# the view (and later middleware) are called.
start_time = time.time()
print(f"Request started: {request.path}")
# Call the next middleware or the view
response = self.get_response(request)
# 2. Code to be executed for each request/response AFTER
# the view is called.
duration = time.time() - start_time
# Add a custom header to the response
response['X-Page-Generation-Duration-ms'] = int(duration * 1000)
print(f"Request finished. Duration: {duration:.4f}s")
return response
def process_exception(self, request, exception):
# Optional: Called if the view raises an exception
print(f"Something went wrong: {exception}")
return None # Let standard error handling continueExplanation
Middleware is a framework of hooks into Django's request/response processing. It's a light, low-level "plugin" system for globally altering Django's input or output. Middleware is initialized once when the server starts (via __init__) and then called for every request.
The __call__ method is the heart of the middleware. Code before get_response(request) runs on the way "in" (request processing), and code after it runs on the way "out" (response processing). You can also define specific hooks:
process_view: Executed just before the view is called. Useful for pre-view validation.process_exception: Executed only if the view raises an exception. Ideal for custom error logging or reporting.process_template_response: Executed for template responses, allowing you to modify the context data before rendering.
The order of middleware in settings.MIDDLEWARE is critical. It forms an "onion" structure: the first middleware defined is the first to see the request and the last to see the response. This is why security middleware is usually placed at the top, while compression middleware is placed near the bottom.
Code Breakdown
__init__. Receives get_response, which is a callable representing the next middleware in the chain (or the actual view). You must store this.__call__. This makes the class instance callable. This method is the heart of the middleware, running for every single request.response = self.get_response(request). This line divides the "Before View" logic from the "After View" logic. It passes control down the chain.X-Page-Generation-Duration-ms. This is a common pattern for performance monitoring tools.
