Python Django中视图的多个装饰器:执行顺序
我试图用两个装饰器装饰Django视图,一个用于检查登录,另一个用于检查是否处于活动状态 第一个是内置的Python Django中视图的多个装饰器:执行顺序,python,django,decorator,Python,Django,Decorator,我试图用两个装饰器装饰Django视图,一个用于检查登录,另一个用于检查是否处于活动状态 第一个是内置的@login\u required,第二个是: def active_required(function): dec = user_passes_test(lambda u: u.is_active, '/notallowed', '') return dec(function) def foo(request): ... foo = login_required(a
@login\u required
,第二个是:
def active_required(function):
dec = user_passes_test(lambda u: u.is_active, '/notallowed', '')
return dec(function)
def foo(request):
...
foo = login_required(active_required(foo))
现在,Python中的装饰器是由内而外工作的,但是以下情况不起作用:
@active_required
@login_required
def foo(request):
...
我想首先检查用户是否已登录,如果未登录,则重定向到登录页面;如果用户已登录,则检查其是否处于活动状态,如果未处于活动状态,则执行重定向到“/notallowed”
如果所需登录失败,则不会将用户重定向到登录页面,而是执行@active\u required
,并且由于在这种情况下用户为空,因此@active\u required decorator失败,用户被重定向到/notallowed
改变顺序似乎有效
@login_required
@active_required
def foo(request):
...
但我怀疑这种方法也有问题
组合两个decorator的正确方法是什么?为什么执行顺序不同于简单的Python decorator?只有当堆栈decorator具有真正独特的功能时,堆栈decorator才有意义。根据您的描述,您永远不会希望使用
active\u required
而不是login\u required
。因此,需要一个login\u和\u active\u
decorator来相应地检查和分支,这样做更有意义。少输入,少记录,并否定问题
现在,Python中的装饰器是从内到外工作的
我想这取决于你对“由内而外”的定义。在您的情况下,您希望首先执行@login\u required
,因此它应该是“最外层”(顶部)的装饰器
正如您所指出的,您的最后一个示例是有效的,并且确实是正确的方法
编辑
令人困惑的可能是这些特殊的装饰师是如何工作的
@login\u required(@original\u view)
返回一个新视图,该视图首先检查您是否已登录,然后调用original\u view
所以
装饰符是按照它们在源代码中出现的顺序应用的。因此,您的第二个示例:
@login_required
@active_required
def foo(request):
...
相当于以下内容:
def active_required(function):
dec = user_passes_test(lambda u: u.is_active, '/notallowed', '')
return dec(function)
def foo(request):
...
foo = login_required(active_required(foo))
因此,如果一个decorator的代码依赖于另一个decorator设置(或确保)的某些内容,那么您必须将依赖的decorator“放在”dedended on decorator中
然而,正如Chris Pratt所指出的,您应该避免具有装饰器依赖性;必要时,创建一个新的decorator,以正确的顺序调用这两个函数。进一步解释一下(一开始我也有点困惑):active\u required
首先应用,因为它需要my\u view
并将其包装在一些代码中。然后应用login\u required
,并将结果包装在更多代码中
但是,当实际调用此包装版本的
my_view
时,首先执行login\u required
添加的代码(检查您是否登录),然后执行active\u required
添加的代码(检查您是否处于活动状态),最后执行my_view
。Hmm,我仍然对顺序有点困惑:并提出其他建议。好吧,我想你已经搞定了,区别在于返回函数和调用函数。好吧,但我想要的正好相反:首先,应该应用login\u required,然后是active\u required。那么,它不应该像我给出的第一个示例中那样吗?在这种情况下,首先应用login\u required
——一种思考方式是对foo
的调用首先通过login\u required
返回的函数,然后通过active\u required
返回的函数。您可以使用PDB逐步完成它,或者添加调试print
s来了解我的意思。好吧,我想Django内置代码会比自定义代码更可靠。Django没有这个装饰器有点奇怪,不过,它应该很常见。我看到一些标记为WONTFIX.approved的bug报告。由于是活动的
是内置的,并且在大多数情况下几乎否定了所需的登录
,因此开发人员应该在开箱即用的情况下考虑到这一点,但这是一个很好的区分方法,可以消除注释中的混乱: