Python 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

我试图用两个装饰器装饰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(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报告。由于
是活动的
是内置的,并且在大多数情况下几乎否定了
所需的登录
,因此开发人员应该在开箱即用的情况下考虑到这一点,但这是一个很好的区分方法,可以消除注释中的混乱: