问题:
我正在尝试访问中间件层中视图实例的属性。
例如,给定这样一个基于类的视图:
# 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(自我,请求,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
没有内置的方法可以做到这一点,但这是 django-users 邮件列表上一位好心的用户给我的解决方案。我在这里重新发布他的建议,以防其他人试图做同样的事情。
这在以下情况下很有用:
这会检查传递给
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。如果你只想标记一个类让中间件做一些事情
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)
另一个解决方案可能是创建一个新的视图类:
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)
如果它取决于视图,它可能应该是该视图的混合。如果你正在做一些依赖于活动视图的菜单之类的事情,我会反向查找当前的 URL 名称: