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 单步执行,或添加调试 print
s 以了解我的意思。【参考方案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中视图的多个装饰器:执行顺序的主要内容,如果未能解决你的问题,请参考以下文章