Django中视图的多个装饰器:执行顺序

时间:2012-01-03 16:50:57

标签: python django decorator

我试图用两个装饰器来装饰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装饰器不同?

4 个答案:

答案 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