django - 装饰 Django 调度方法的最简洁方法
问题描述
我偶然发现了一个用于为请求方法提供一些参数的代码。问题是我不确定这是否是处理这种情况的最干净的方法。
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))
为什么是 data 而不是 self.data ?这怎么可能 ?
def dispatch(self, request, *args, **kwargs):
return super(SecurityMixin, self).dispatch(request, *args, **kwargs)
然后每个视图都从SecurityMixin继承并authenticators = [UserLoginMixin, ...]
作为类属性获取。
我有时遇到的问题(我无法重现错误......)是在正确设置 URL 定义时,我在增强的 kwargs 上遇到了 KeyError。例如:
appo_id = kwargs['appo_id']
KeyError: 'appo_id'Exception
我一直在寻找几个小时,似乎我永远不会有解决方案......这有点令人沮丧。
如果有人可以提供帮助,将不胜感激。
解决方案
我有一种预感,对类属性的不当处理是错误的。
类与实例
每次调用时都会覆盖类属性: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' 参数),包装函数确实可以访问实例。
Djangomethod_decorator
删除了这个 self 参数;该装饰器用于将函数装饰器(不self
隐式获取参数)转换为方法装饰器(self
隐式获取参数)。这就是为什么您不必self
在各种 mixin 检查方法的参数列表中包含它,因为它已被method_decorator
. 简单来说:用method_decorator
函数装饰器来装饰方法。在此处阅读装饰 CBV的内容。
知道了这一点,我不确定为什么当你只使用它来装饰方法时,为什么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”。您也可以通过将所需的检查混合直接传递给装饰器来尝试显式:@method_decorator(check_permissions([UserLoginMixin, ...]))
. 这样您就不必弄乱类与实例属性。
另外......你应该重命名data
为你不可能用你自己的变量覆盖的东西。
如果你想变得超级懒惰,你可以这样做:
appo_id = kwargs.get('appo_id',False)
if not appo_id: return kwargs
但这只会修复该视图中的特定错误。它忽略了症状而不是治愈疾病。
更多解释:
功能与方法。check_permissions
是函数,whiledispatch()
是方法。您不能简单地在方法上使用函数装饰器:一方面,因为隐式参数(方法所属self
的实例)也会传递给装饰器,尽管它可能不会期望它。
这就是 django通过在装饰器中删除和存储而发挥作用的地方。比较两个签名:vs 。在前者中,在调用函数装饰器之前“吸收” 。把它想象成一个适配器,一个装饰器的装饰器,它“弥合了函数和方法之间的鸿沟”。如果您不想/不能更改装饰器,请使用它。method_decorator
self
wrapper(request, *args, **kwargs)
_decorator(self, request, *args, **kwargs)
method_decorator
self
但是,在您的情况下,您可以更改装饰器以使其与方法一起使用 - 因此您不需要 django 的method_decorator
.
推荐阅读
- git - 无法将我的分支合并到 master
- javascript - Chart.js:条形图显示标签,而不是数据
- php - 按名称排序数组 PHP
- javascript - 如何从当前页面传递值并在新选项卡中显示这些值
- android - Google Places API:未捕获运行时异常
- json - AWS CLI / jq - 使用标签转换 JSON,甚至显示未定义标签的信息
- python - ("由于模型数据错误,无法加载模型。标签:['serve']\n在定义的操作中没有名为 HashTableV2 的操作。", 1)
- java -
antlr4 不匹配 - c++ - 如何将 i386 代码片段链接到 x86_64 精灵?
- c++ - C++ 检查十六进制是否由 ABCDEF1 或 0 组成