我偶然发现了一个代码,用于为请求方法提供一些参数。问题是,我不确定是否是最干净的方式来处理这种情况。
def check_permissions(check_mixins):
"""
:param check_mixins: is given to the inner decorator
Decorator that will automatically populate some parameters when
using dispatch() toward the right method (get(), post())
"""
def _decorator(_dispatch):
def wrapper(request, *args, **kwargs):
如果在这里的方法定义中没有传递“self”,这是一个问题......
for mixin in check_mixins:
kwargs = mixin.check(request, *args, **kwargs)
if isinstance(kwargs, HttpResponseRedirect):
return kwargs
return _dispatch(request, *args, **kwargs)
return wrapper
return _decorator
class UserLoginMixin(object):
def check(request, *args, **kwargs):
......在这儿?在我的IDE中它看起来很丑陋
user = request.user
if user.is_authenticated() and not user.is_anonymous():
kwargs['user'] = user
return kwargs
return redirect('user_login')
class AppoExistMixin(object):
def check(request, *args, **kwargs):
这里也是......
appo_id = kwargs['appo_id']
try:
appoff = IdAppoff.objects.get(id=appo_id)
kwargs['appoff'] = appoff
del kwargs['appo_id']
return kwargs
except IdAppoff.DoesNotExist:
pass
messages.add_message(request, messages.ERROR,
"Item doesn't exist!")
return redirect('home')
class SecurityMixin(View):
"""
Mixin that dispatch() to the right method with augmented kwargs.
kwargs are added if they match to specific treatment.
"""
data = []
def __init__(self, authenticators):
super(SecurityMixin, self).__init__()
# Clearing data in order to not add useless param to kwargs
self.data.clear()
# Build the list that contain each authenticator providing
# context increase
for auth in authenticators:
self.data.append(auth)
@method_decorator(check_permissions(data))
为什么数据而不是self.data?怎么可能?
def dispatch(self, request, *args, **kwargs):
return super(SecurityMixin, self).dispatch(request, *args, **kwargs)
然后,每个视图都继承自 SecurityMixin ,并将authenticators = [UserLoginMixin, ...]
作为类属性。
我有有时(我无法重现错误...)的问题是我在增强的kwargs上获得了KeyError,同时正确设置了URL定义。例如:
appo_id = kwargs['appo_id']
KeyError: 'appo_id'Exception
我一直在寻找好几个小时,似乎我永远不会有解决方案......这有点令人沮丧。
如果有人可以提供帮助,我们将非常感激。
答案 0 :(得分:0)
我有一种预感,即对类属性的不当处理是错误的。
CLASS VS INSTANCE
每次调用data
时都会覆盖类属性SecurityMixin.__init__
:
class A:
data = []
def __init__(self, *args):
self.data.clear() # self.data references the class attribute
for x in args:
self.data.append(x)
x = A('foo')
# A.data = ['foo']
# x.data = ['foo']
y = A('bar')
# A.data = ['bar']
# y.data = ['bar']
# x.data = ['bar'] !!
但是:
class A:
data = ['I am empty']
def __init__(self, *args):
self.data = [] # redeclaring data as an instance attribute
for x in args:
self.data.append(x)
x = A('foo')
# A.data = ['I am empty']
# x.data = ['foo']
y = A('bar')
# A.data = ['I am empty']
# y.data = ['bar']
# x.data = ['foo']
此类属性data
被传递给装饰器(您无法将实例属性传递给方法装饰器,即self.data,因为在装饰器声明期间该实例尚不存在)。
但是,如果传入(' self'参数),则包装函数可以访问实例。
Django' method_decorator
删除了这个自我论点;该装饰器用于将函数装饰器(它没有隐式获得self
参数)转换为方法装饰器(获取self
参数隐含地)。这就是为什么您不必在self
删除各种mixin检查方法的参数列表中包含method_decorator
的原因。简单地说:使用method_decorator
用函数装饰器来装饰方法。请在此处阅读decorating CBVs。
知道这一点,我不确定为什么check_permissions
应该是函数装饰器,因为现在只使用它来装饰方法。
您可以使用check_permissions
本身装饰调度:
def check_permissions(_dispatch):
def _decorator(self, request, *args, **kwargs): # adding self
for mixin in self.data: # referencing the INSTANCE data
kwargs = mixin.check(request, *args, **kwargs)
if isinstance(kwargs, HttpResponseRedirect):
return kwargs
return _dispatch(self, request, *args, **kwargs) # don't forget self here
return _decorator
@check_permissions
def dispatch(self, request, *args, **kwargs):
...
也许某些视图正在尝试检查AppoExistMixin
,因为它位于该视图的数据列表中,尽管它不应该是 - 并且视图的kwargs不包含' appo_id& #39 ;.您也可以通过将所需的check mixins直接传递给装饰器来尝试显式:@method_decorator(check_permissions([UserLoginMixin, ...]))
。通过这种方式,您不必弄乱类和实例属性。
此外......您应该将data
重命名为您不太可能使用自己的变量覆盖的内容。
如果你想成为超级懒人,你可以这样做:
appo_id = kwargs.get('appo_id',False)
if not appo_id: return kwargs
但这只会修复那个视图中的特定错误。它忽视了一种症状,而不是治愈这种疾病。
更多解释:
功能与方法。 check_permissions
是函数,而dispatch()
是方法。你不能简单地在方法上使用函数装饰器:首先,因为隐式参数self
(方法所属的实例)也被传递给装饰器,尽管它可能不会期待它。
这就是django的method_decorator
通过在装饰器中移除和存储self
而进入的地方。比较两个签名:wrapper(request, *args, **kwargs)
vs _decorator(self, request, *args, **kwargs)
。在前者,method_decorator
'吸收'调用函数装饰器之前self
。
可以把它想象成一个适配器,一个装饰器的装饰器,它可以弥补差距。功能和方法之间。如果您不想/不能改变装饰者,请使用它
但是,在您的情况下,可以更改装饰器以使其与方法一起使用 - 因此您不需要django' method_decorator
。