Django Custom Middleware
Django middleware is a way to globally alter requests and responses ie. middlewares are layers of business logic that will be applied to all requests and responses. Consider an example to add logging for all requests, then the ideal way would be to add a middleware layer for the same, another example would be adding a common header to all the responses and again middleware is the best place to add this.
As per Django documentation:
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.
Technically speaking, Middleware is a normal Python class that should have at least one of these methods defined.
- process_request
- process_view
- process_exception
- process_template_response
- process_response
Please note that middlewares are not exclusive to Django, it is a concept that is applicable to any request-response cycle written in any language/framework.
Where can I find middleware in my Django project?
In Django, you can see all the default installed
middlewares in the settings.py
module. Please do not remove any, unless you know what you are doing.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Does ordering matter in middlewares?
Yes, ordering of layers matters in middlewares, as logic written in a layer below, can expect something added from the layers above. Let's understand this with an example if AuthenticationMiddleware
is present before SessionMiddleware
, it will raise AssertionError
because AuthenticationMiddleware
expects request object to have session
attribute, which is added by SessionMiddleware
.
class AuthenticationMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE_CLASSES setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
)
request.user = SimpleLazyObject(lambda: get_user(request))
The Middleware classes are called twice during the request/response life cycle, during the request cycle, the middleware classes are executed top-down, while during the response, middleware classes are called in a bottom-up manner.
Custom Middleware
Let's add a custom middleware to our Django project which logs the total time taken by the view. In order to install a middleware, we need to put it in settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
...
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'common.middleware.LogTimeTaken',
]
from django.utils import timezone
class LogTimeTaken(object):
def process_request(self, request):
request.start_time = timezone.now()
def process_response(self, request, response):
total_time = timezone.now() - request.start_time
print('Time taken: {}'.format(total_time))
Conclusion:
- Middlewares as a concept is a way to globally alter request-response behavior and is not exclusive to Django.
- In Django specifically, middlewares are normal Python classes with certain methods. These classes are called twice during the request/response life cycle, top-down while in the request phase, and reverse during in response.
- Ordering matters, as a middleware can depend on other middleware during its execution.