Django middlewares with Rest Framework

Due to some design decisions in Django Rest Framework, the request.user is not available in the middleware request layer but only the views layer. Which means any extensible middlewares that you can think of getting, will not have access to the user object. Some examples of top off my head: apps like django-auditlog, django-simple-history, a middleware which customizes API responses based on user attributes, etc.

Below is a simple middleware which does Token Authentication allowing the request.user object to be populated everywhere. Note: This is based on this brilliant answer.

A simple snippet below makes the request.user object available using Token authentication.

class RestAuthMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    @staticmethod
    def get_user(request):
        user = get_user(request)
        if user.is_authenticated:
            return user

        token_authentication = TokenAuthentication()
        try:
            user, token = token_authentication.authenticate(request)
        except:
            pass
        return user

    def __call__(self, request):
        request.user = SimpleLazyObject(lambda: self.__class__.get_user(request))

        response = self.get_response(request)
        return response

This middleware can just be placed above any middleware which needs access to the request object, will run with any other middlewares that depend on request.user object.

Custom User Response language middleware

At the same time, we had a requirement to serve API results in the user’s language, so instead of using the standard Language middleware, I implemented a simple custom middleware which uses the user’s language (and also override it using the lang=hi parameter).
Building a language middleware is easy:

class LanguageMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        language = request.user.language_code()
        # Override language using query parameter
        language = request.GET.get('lang', language)
        
        if language is not None:
            activate(language)

        response = self.get_response(request)

        if language is not None:
            deactivate()

        return response

Leave a comment