访问Django中间件中的当前视图类实例

时间:2021-08-12 15:53:46

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:

我希望能够通过这样的方式获得中间件中my_attribute的句柄:

# 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?

当然,这不起作用,因为Django不会通过请求对象公开视图实例。有没有办法实现这一目标?

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()方法可能是一个很好的地方。不幸的是,它接收的view_func参数包含一个函数 - MyView.as_view()的输出 - 而不是视图实例本身。来自Django文档:

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

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.)...

... view_func是Django即将使用的Python函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)...


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:

process_template_response()方法中提供了视图实例的句柄,但它非常笨拙,而且,无论如何,我希望能够在中间件堆栈的早期阶段使用my_attribute。但这确实有效:

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

3 个解决方案

#1


3  

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.

没有内置的方法可以做到这一点,但这是一个由django-users邮件列表上的善良用户给我的解决方案。我在这里重新提出他的建议,以防其他人试图做同样的事情。

This is useful if:

这在以下情况下很有用

  1. you want to identify properties of the current view in your middleware and perform processing accordingly, and;
  2. 您希望在中间件中识别当前视图的属性并相应地执行处理,并且;

  3. for various reasons you don't want to use mixins or decorators to accomplish similar results.
  4. 由于各种原因,您不希望使用mixins或decorator来实现类似的结果。

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

这将检查传递给process_view()中间件钩子的view_func对象,并确定并导入相应的视图类。

# 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:

然后你的get_class()定义:

# 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

#2


1  

Another solution could be to create a new View class:

另一种解决方案可能是创建一个新的View类:

from django.views.generic.base import View
class AddClassView(View):
    @classonlymethod
    def as_view(cls, **initkwargs):
        view = super(AddClassView, cls).as_view(**initkwargs)
        view.cls = cls
        return view

And use this in your class based view:

并在基于类的视图中使用它:

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

Then you do the following in the middleware:

然后在中间件中执行以下操作:

# middleware.py
def process_view(self, request, view_func, view_args, view_kwargs):
    view_func.cls.my_attribute  # 'something'

This method is used in the Django REST Framework(https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py#L94-L104)

此方法用于Django REST框架(https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py#L94-L104)

#3


0  

If it depends on the view, it should probably be a mixin of that view. If you're doing something like a menu that depends on the active view, I'd do a reverse lookup of the current URL name:

如果它取决于视图,它可能应该是该视图的混合。如果您正在执行类似于依赖于活动视图的菜单,我会反向查找当前URL名称:

see a previous answer about using URL name lookup of the current URL

查看有关使用当前URL的URL名称查找的上一个答案

#1


3  

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.

没有内置的方法可以做到这一点,但这是一个由django-users邮件列表上的善良用户给我的解决方案。我在这里重新提出他的建议,以防其他人试图做同样的事情。

This is useful if:

这在以下情况下很有用

  1. you want to identify properties of the current view in your middleware and perform processing accordingly, and;
  2. 您希望在中间件中识别当前视图的属性并相应地执行处理,并且;

  3. for various reasons you don't want to use mixins or decorators to accomplish similar results.
  4. 由于各种原因,您不希望使用mixins或decorator来实现类似的结果。

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

这将检查传递给process_view()中间件钩子的view_func对象,并确定并导入相应的视图类。

# 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:

然后你的get_class()定义:

# 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

#2


1  

Another solution could be to create a new View class:

另一种解决方案可能是创建一个新的View类:

from django.views.generic.base import View
class AddClassView(View):
    @classonlymethod
    def as_view(cls, **initkwargs):
        view = super(AddClassView, cls).as_view(**initkwargs)
        view.cls = cls
        return view

And use this in your class based view:

并在基于类的视图中使用它:

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

Then you do the following in the middleware:

然后在中间件中执行以下操作:

# middleware.py
def process_view(self, request, view_func, view_args, view_kwargs):
    view_func.cls.my_attribute  # 'something'

This method is used in the Django REST Framework(https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py#L94-L104)

此方法用于Django REST框架(https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py#L94-L104)

#3


0  

If it depends on the view, it should probably be a mixin of that view. If you're doing something like a menu that depends on the active view, I'd do a reverse lookup of the current URL name:

如果它取决于视图,它可能应该是该视图的混合。如果您正在执行类似于依赖于活动视图的菜单,我会反向查找当前URL名称:

see a previous answer about using URL name lookup of the current URL

查看有关使用当前URL的URL名称查找的上一个答案