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

时间:2013-12-21 04:48:50

标签: django django-class-based-views django-middleware

问题:

我正在尝试访问中间件层中的视图实例的属性。

例如,给定一个基于类的视图:

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

我希望能够通过这样的方式处理中间件中my_attribute的处理:

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

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

谢谢!


我的第一次尝试:

我最初认为process_view()方法可能是一个很好的地方。不幸的是,它收到的view_func参数包含一个函数 - MyView.as_view()的输出 - 而不是视图实例本身。来自Django docs

  

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

     

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


我的第二次尝试:

process_template_response()方法提供了视图实例的句柄,但它非常尴尬,而且,无论如何,我希望能够在之前的某个时间点使用my_attribute。中间件堆栈。但这确实有效:

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

4 个答案:

答案 0 :(得分:3)

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

如果符合以下条件,这非常有用:

  1. 您希望在中间件中识别当前视图的属性并执行相应的处理,并且;
  2. 由于各种原因,您不想使用mixins或decorator来实现类似的结果。
  3. 这将检查传递给view_func中间件钩子的process_view()对象,并确定并导入相应的视图类。

    # 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
    

    然后是您的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
    

答案 1 :(得分:1)

另一种解决方案可能是创建一个新的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

在基于班级的视图中使用它:

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

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

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

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

使用装饰器,有很多方法可以实现所需的行为。

1。如果您只想标记一个中间件类来做某事

onChange

2。如果您要将一些不需要的数据传递给中间件,

Field

3。如果您想向中间件公开一些视图属性

from django.utils.decorators import classonlymethod

def special_marker(class_view):
    def as_view(cls, **initkwargs):
        view = super(cls, cls).as_view(**initkwargs)
        view.special_marker = True
        return view
    return type(class_view.__name__, (class_view,), {
        'as_view': classonlymethod(as_view),
    })


@special_marker
class MyView(View):
    pass


class MyMiddleware:

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

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

    def process_view(self, request, view_func, view_args, view_kwargs):
        special_marker = getattr(view_func, 'special_marker', False)
        if special_marker:
            # Do something