我试图用两个装饰器来装饰Django视图,一个用于检查登录,另一个用于检查is_active。
第一个是内置@login_required
,第二个是以下内容:
def active_required(function):
dec = user_passes_test(lambda u: u.is_active, '/notallowed', '')
return dec(function)
现在,Python中的装饰器内部工作,但以下方法不起作用:
@active_required
@login_required
def foo(request):
...
我想首先检查用户是否已登录,如果没有,则重定向到登录页面,如果他或她已登录,我想检查他或她是否处于活动状态,如果没有,则执行重定向到'/notallowed'
。
如果login_required失败,用户不会被重定向到登录页面,但会执行@active_required
,并且由于用户在这种情况下为空,@ active_required装饰器失败并且用户被重定向到/notallowed
。
更改订单似乎有效,
@login_required
@active_required
def foo(request):
...
但我怀疑这种方法也有问题。
组合两个装饰器的正确方法是什么,为什么执行顺序与简单的Python装饰器不同?
答案 0 :(得分:32)
现在,Python中的装饰器内部工作
嗯,我想这取决于你对内到外的定义。在你的情况下,你想首先执行login_required
,所以它应该是“最外层”(顶部)装饰器
正如您所指出的,您的上一个示例有效,并且确实是执行此操作的正确方法
修改强>
也许混淆是(这些特定的)装饰器如何工作
login_required(original_view)
返回一个新视图,首先检查您是否已登录,然后调用original_view
所以
login_required(
active_required(
my_view
)
)
first checks if you are logged in, then
first(second) checks if you are active, then
runs my_vew
答案 1 :(得分:14)
装饰器按照它们在源中出现的顺序应用。因此,你的第二个例子:
@login_required
@active_required
def foo(request):
...
等同于以下内容:
def foo(request):
...
foo = login_required(active_required(foo))
因此,如果一个装饰器的代码依赖于由另一个装饰器设置(或由另一个装饰确定),则必须将依赖装饰器“置于”depdended-on装饰器内部。
但是,正如Chris Pratt所说,你应该避免使用装饰器依赖;必要时,创建一个新的装饰器,以正确的顺序调用它们。
答案 2 :(得分:11)
如果装饰器具有真正独特的功能,那么它们才真正有意义。根据您的描述,您永远不会想要使用active_required
但不 login_required
。因此,让login_and_active_required
装饰器相应地检查两者和分支更有意义。少输入,少记录,否定问题。
答案 3 :(得分:4)
要解释一下(我最初也感到困惑):首先应用active_required
,因为它需要my_view
并将其包装在某些代码中。然后应用login_required
并将结果包含在更多代码中。
但实际调用my_view
的这个包装版本时,首先执行login_required
添加的代码(检查您是否已登录),然后由active_required
添加的代码执行(检查您是否处于活动状态)然后最终执行my_view
。