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

Posted

技术标签:

【中文标题】Django中视图的多个装饰器:执行顺序【英文标题】:Multiple decorators for a view in Django: Execution order 【发布时间】:2012-02-01 16:36:22 【问题描述】:

我正在尝试用两个装饰器来装饰 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被执行了,由于这种情况下用户为null,@active_required装饰器失败,用户被重定向到@ 987654326@.

更改顺序似乎有效,

@login_required
@active_required
def foo(request):
    ...

但我怀疑这种方法也有问题。

组合两个装饰器的正确方法是什么,为什么执行顺序与简单的 Python 装饰器不同?

【问题讨论】:

【参考方案1】:

现在,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_view

【讨论】:

嗯,我对这个顺序还是有点困惑:***.com/a/739665/72436 和 ***.com/a/8715839/72436 建议不然。 好的,我想你搞定了,区别在于返回一个函数和调用它。【参考方案2】:

装饰器按照它们在源代码中出现的顺序应用。因此,您的第二个示例:

@login_required
@active_required
def foo(request):
    ...

等价于:

def foo(request):
    ...
foo = login_required(active_required(foo))

因此,如果一个装饰器的代码依赖于另一个装饰器设置(或确保)的东西,则必须将依赖的装饰器放在依赖的装饰器“内部”。

但是,正如 Chris Pratt 所说,您应该避免使用装饰器依赖项;必要时,创建一个新的装饰器,以正确的顺序调用两者。

【讨论】:

好的,但我想要反过来:首先,应该应用 login_required,然后是 active_required。那么,不应该像我给出的第一个例子那样吗? 在这种情况下,首先应用login_required——一种思路是,对foo的调用先经过login_required返回的函数,再经过返回的函数通过active_required。您可以使用 PDB 单步执行,或添加调试 prints 以了解我的意思。【参考方案3】:

只有当装饰器具有真正独特的功能时,堆叠装饰器才真正有意义。根据您的描述,永远不会出现您想要使用active_required login_required 的情况。因此,拥有一个 login_and_active_required 装饰器来检查两者并相应地分支会更有意义。少打字,少记录,解决问题。

【讨论】:

好吧,我认为 Django 内置函数会比自定义代码更可靠。 Django 没有这个装饰器有点奇怪,不过,它应该很常见。我看到了一些标记为 WONTFIX 的错误报告。 同意。由于is_active 是内置的,并且在大多数情况下几乎否定了login_required,因此开发人员应该开箱即用地考虑到这一点,但只能看到。【参考方案4】:

再解释一下(起初我也很困惑):首先应用active_required,因为它需要my_view并将其包装在一些代码中。然后应用login_required 并将结果包装在更多代码中。

但是当实际调用my_view 的这个包装版本时,首先执行login_required 添加的代码(检查您是否已登录),然后执行active_required 添加的代码(检查您'reactive) 然后最后my_view 被执行。

【讨论】:

这是区分 cmets 的好方法:***.com/a/8715821/781695

以上是关于Django中视图的多个装饰器:执行顺序的主要内容,如果未能解决你的问题,请参考以下文章

Python装饰器执行顺序详解

多个装饰器执行顺序

python多个装饰器的执行顺序

python多个装饰器的执行顺序

python多个装饰器的执行顺序

python 多个装饰器的调用顺序