Hacks by Ruddra

Django Access URL Arguments Through Middleware

Django Access URL Arguments Through Middleware

Lets say you have an API which is being used by many stores. Now for a specific store ABC, they want to have a different response format. So how would you make this implementation generic without making different views for store ABC? Simple, use middleware.

How It Works

First update the url like this:

url = [
    path('api/v1/<str:store_code>/', include("api.urls")),
]

Then write a middleware to capture the value of store_code:

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

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_view(request, view_func, view_args, view_kwargs):
        store_code = view_kwargs.get('store_code', None)
        if store_code:
            request.session['store_code'] = store_code

Here I am using process_view to capture the url argument. Because in process_view, there is a keyword argument named view_kwargs which is a dictionary, which will be passed to the view. Now, we can attach the store_code to request object through another middleware.

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

    def __call__(self, request):
        store_code = request.session.get('store_code', None)
        if store_code:
             request.store, _ = Store.objects.get_or_create(code=store_code)  # assuming `Store` is the model class where store information are being stored
        response = self.get_response(request)
        return response

Now lets add these middlewares in settings.py:

MIDDLEWARES = [
    # rest of the middlewares
    'path.to.StoreCodeMiddleware',
    'path.to.StoreToRequestMiddleware'
]

Make sure StoreCodeMiddleware is on top of StoreToMiddleware.

Usage

Lets say you have an API view, then use store info like this:

class StoreAPI(APIView):
    def get(self, request, *args, **kwargs):
        serializer = CommonSerializer(request.store)
        if request.store.code == "ABC":
            serializer = ABCSerializer(request.store)
        return Response(serializer.data)

Another Usage Scenario

Lets say you are using a package like black box which provides everything through url(means you can’t override anything inside the views or models), then the approach is useful. For example, when using djoser or django-rest-auth, they provide authentication services(like login, registration, password reset etc), you just need to include their url in your root url. When using these libraries, you might need to store additional information coming through url; then the given approach is really helpful.

Hope it helps. Thanks for reading. Cheers!!