在Django中间件中访问当前视图类示例

ddhy6vgd  于 2023-05-01  发布在  Go
关注(0)|答案(4)|浏览(120)

问题:

我正在尝试访问中间件层中视图示例的一个属性。
例如,给定一个像这样的基于类的视图:

# 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没有通过request对象公开视图示例。有没有办法做到这一点?
谢谢!

第一次尝试:

我最初认为process_view()方法可能是完成此任务的好地方。不幸的是,它接收的view_func参数包含一个函数--MyView.as_view()的输出--而不是视图示例本身。Django文档:

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
w1e3prcc

w1e3prcc1#

没有内置的方法可以做到这一点,但这里有一个由django-users邮件列表上的一位好心的用户给我的解决方案。我在这里重新发布他的建议,以防其他人试图做同样的事情。
这在以下情况下很有用:
1.您希望在中间件中识别当前视图的属性并相应地执行处理,并且;
1.出于各种原因,您不希望使用mixin或装饰器来实现类似的结果。
这将检查传递给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

然后你的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
7d7tgy0s

7d7tgy0s2#

使用装饰器,有相当多的方法来实现期望的行为。

1.如果您只想标记一个类,让中间件做一些事情

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

2.如果您想将视图中不需要的数据传递给中间件

from django.utils.decorators import classonlymethod

def tell_middleware(**kwargs):
    def wrapper(class_view):
        def as_view(cls, **initkwargs):
            view = super(cls, cls).as_view(**initkwargs)
            for k, v in kwargs.items():
                setattr(view, k, v)
            return view
        return type(class_view.__name__, (class_view,), {
            'as_view': classonlymethod(as_view),
        })
    return wrapper

@tell_middleware(my_attribute='something')
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):
        my_attribute = getattr(view_func, 'my_attribute', 'default value')
        if my_attribute == 'something':
            # Do something

3.如果需要向中间件暴露一些视图属性

from django.utils.decorators import classonlymethod

def expose_to_middleware(*args):
    def wrapper(class_view):
        def as_view(cls, **initkwargs):
            view = super(cls, cls).as_view(**initkwargs)
            for attr in args:
                setattr(view, attr, getattr(class_view, attr)
            return view
        return type(class_view.__name__, (class_view,), {
            'as_view': classonlymethod(as_view),
        })
    return wrapper

@expose_to_middleware('my_attribute', 'my_other_attribute')
class MyView(View):
    my_attribute = 'something'
    my_other_attribute = 'else'
    unexposed_attribute = 'foobar'

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):
        my_attribute = getattr(view_func, 'my_attribute', 'default value')
        if my_attribute == 'something':
            # Do something

4.如果要将基于类的整个视图公开给中间件

from django.utils.decorators import classonlymethod

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

@expose_cbv_to_middleware
class MyView(View):
    my_attribute = 'something'

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):
        cbv = getattr(view_func, 'cbv', None)
        if cbv:
            if hasattr(cbv, 'my_attribute'):
                print(cbv.my_attribute)
m4pnthwp

m4pnthwp3#

另一种解决方案是创建一个新的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 Framework(https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views。py#L94-L104)

34gzjxbg

34gzjxbg4#

如果它依赖于视图,那么它应该是该视图的混合。如果你正在做一些类似于依赖于活动视图的菜单的事情,我会对当前URL名称进行反向查找:
查看前面关于使用当前URL的URL名称查找的答案

相关问题