djangodjango-class-based-viewsdjango-middleware

Accessing the current view class instance in Django middleware


Question:

I'm trying to access an attribute of the view instance in the middleware layer.

For example, given a class-based view like this:

# views.py
class MyView(View):
    my_attribute = 'something'

I'd love to be able to get a handle on my_attribute in the middleware by doing something like this:

# middleware.py
def process_view(self, request, view_func, view_args, view_kwargs):
    my_attribute = request.view.my_attribute

Of course, this does not work because Django doesn't expose the view instance through the request object. Is there a way to get this accomplished?

Thanks!


My first attempt:

I initially figured that the process_view() method might be a good place to do this. Unfortunately, the view_func argument it receives contains a function -- the output of MyView.as_view() -- rather than the view instance itself. From the Django docs:

process_view(self, request, view_func, view_args, view_kwargs)

...view_func is the Python function that Django is about to use. (It’s the actual function object, not the name of the function as a string.)...


My second attempt:

A handle to the view instance is available in process_template_response() method, but it's pretty awkward, and, in any case, I'd like to be able to work with my_attribute at an earlier point in the middleware stack. But this does work:

def process_template_response(self, request, response):
    my_attribute = response.context_data['view'].my_attribute

Solution

  • There is no built-in way to do this, but here is a solution given to me by a kindly user on the django-users mailing list. I'm reposting his suggestion here in case anyone else is trying to do the same thing.

    This is useful if:

    1. you want to identify properties of the current view in your middleware and perform processing accordingly, and;
    2. for various reasons you don't want to use mixins or decorators to accomplish similar results.

    This inspects the view_func object passed to the process_view() middleware hook and determines and imports the the appropriate view class.

    # middleware.py
    from myutils import get_class
    
    def process_view(self, request, view_func, view_args, view_kwargs):
            view = get_class(view_func.__module__, view_func.__name__)
            view.my_attribute
    

    Then your get_class() definition:

    # myutils.py
    from django.utils import importlib
    
    def get_class(module_name, cls_name):
        try:
            module = importlib.import_module(module_name)
        except ImportError:
            raise ImportError('Invalid class path: {}'.format(module_name))
        try:
            cls = getattr(module, cls_name)
        except AttributeError:
            raise ImportError('Invalid class name: {}'.format(cls_name))
        else:
            return cls