是否可以使用 login_required 在 django url 中装饰 include(...)?
Posted
技术标签:
【中文标题】是否可以使用 login_required 在 django url 中装饰 include(...)?【英文标题】:Is it possible to decorate include(...) in django urls with login_required? 【发布时间】:2011-01-19 11:00:30 【问题描述】:我在网站上有一些限制区域,我想指定 login_required
装饰器。但是,我想每次包含在主 urls.py 中执行一次,而不是每个包含 urls.py 中的单个 url
所以而不是:
/private/urls.py:
(r'^profile/$', login_required(profile)),
我会做一些类似的事情:
/urls.py
urlpatterns = patterns('',
...
(r'^private/', login_required(include('private'))),
)
不幸的是,它不起作用。
【问题讨论】:
【参考方案1】:login_required
用于包装可调用的视图,而不是 include(),并查看源代码:
http://code.djangoproject.com/browser/django/tags/releases/1.1.1/django/conf/urls/defaults.py#L9
-- 我认为没有一种简单的方法可以使用默认(甚至自定义)login_required
和 include() 来实现您想要实现的目标。
写到这里,我认为合理的方法是使用一些“需要登录的中间件”,比如这个:http://www.djangosnippets.org/snippets/1179/,忘记在 urls.py 中装饰 url。
【讨论】:
【参考方案2】:这是可行的,事实上我只是为此找到了twosnippets。
解决方案 #1
cotton 的第一个 sn-p 将 RegexURLPattern
和 RegexURLResolver
替换为在 resolve
调用期间注入给定装饰器的自定义实现。
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
from django.conf.urls.defaults import patterns, url, include
from django.contrib import admin
from myproject.myapp.decorators import superuser_required
class DecoratedURLPattern(RegexURLPattern):
def resolve(self, *args, **kwargs):
result = super(DecoratedURLPattern, self).resolve(*args, **kwargs)
if result:
result.func = self._decorate_with(result.func)
return result
class DecoratedRegexURLResolver(RegexURLResolver):
def resolve(self, *args, **kwargs):
result = super(DecoratedRegexURLResolver, self).resolve(*args, **kwargs)
if result:
result.func = self._decorate_with(result.func)
return result
def decorated_includes(func, includes, *args, **kwargs):
urlconf_module, app_name, namespace = includes
for item in urlconf_module:
if isinstance(item, RegexURLPattern):
item.__class__ = DecoratedURLPattern
item._decorate_with = func
elif isinstance(item, RegexURLResolver):
item.__class__ = DecoratedRegexURLResolver
item._decorate_with = func
return urlconf_module, app_name, namespace
你需要像这样使用它:
urlpatterns = patterns('',
# ...
(r'^private/', decorated_includes(login_required, include(private.urls))),
)
(注意include
这个方法的参数不能是字符串。)
解决方案 #2
sjzabel 的另一个解决方案是我自己最终使用的,它被应用在 outside patterns
调用中,因此它可以与字符串一起使用,并且语法略有不同。不过,想法是一样的。
def required(wrapping_functions,patterns_rslt):
'''
Used to require 1..n decorators in any view returned by a url tree
Usage:
urlpatterns = required(func,patterns(...))
urlpatterns = required((func,func,func),patterns(...))
Note:
Use functools.partial to pass keyword params to the required
decorators. If you need to pass args you will have to write a
wrapper function.
Example:
from functools import partial
urlpatterns = required(
partial(login_required,login_url='/accounts/login/'),
patterns(...)
)
'''
if not hasattr(wrapping_functions,'__iter__'):
wrapping_functions = (wrapping_functions,)
return [
_wrap_instance__resolve(wrapping_functions,instance)
for instance in patterns_rslt
]
def _wrap_instance__resolve(wrapping_functions,instance):
if not hasattr(instance,'resolve'): return instance
resolve = getattr(instance,'resolve')
def _wrap_func_in_returned_resolver_match(*args,**kwargs):
rslt = resolve(*args,**kwargs)
if not hasattr(rslt,'func'):return rslt
f = getattr(rslt,'func')
for _f in reversed(wrapping_functions):
# @decorate the function from inner to outter
f = _f(f)
setattr(rslt,'func',f)
return rslt
setattr(instance,'resolve',_wrap_func_in_returned_resolver_match)
return instance
这样称呼它:
urlpatterns = patterns('',
# ...
)
urlpatterns += required(
login_required,
patterns('',
(r'^private/', include('private.urls'))
)
)
两者都可以正常工作,但我更喜欢后一种语法。
【讨论】:
这个功能也可以在一个包中使用,django-decorator-include。 @JoshKelley 的评论给出了完美的答案。最简单的使用方式!【参考方案3】:另一种选择:
def decorate_url(decorator, urlconf):
'''Recreates the url object with the callback decorated'''
# urlconf autoresolves names, so callback will always be a function
return url(urlconf._regex, decorator(urlconf.callback), urlconf.default_args, urlconf.name)
def decorate_include(decorator, urlpatterns):
urls = [
decorate_url(decorator, urlconf) if not isinstance(urlconf, RegexURLResolver) else decorate_include(decorator, urlconf)
for urlconf in urlpatterns[0]
]
return (urls,) + urlpatterns[1:]
# usage
urlpatterns += patterns(
'',
url('^my-url/', decorate_include(login_required, include('app.urls'))),
)
一个稍微复杂的版本,支持多个装饰器:
def compose_decorators(decorators, wrappee):
for wrapper in decorators:
wrappee = wrapper(wrappee)
return wrappee
def decorate_url(urlconf, *decorators):
''' Decorate a url structure with decorators '''
revdecorators = decorators[::-1] # we want the function call to read left to right
# urlconf autoresolves names, so callback will always be a function
return url(
urlconf._regex,
compose_decorators(revdecorators, urlconf.callback),
urlconf.default_args,
urlconf.name
)
def decorate_include(urlpatterns, *decorators):
''' Decorate a patterns structure with decorators '''
urls = [
decorate_url(urlconf, *decorators) if not isinstance(urlconf, RegexURLResolver) else decorate_include(urlconf, *decorators)
for urlconf in urlpatterns[0]
]
return (urls,) + urlpatterns[1:]
# usage
urlpatterns += patterns(
'',
url('^my-url/', decorate_include(include('app.urls'), login_required, decorator2)),
)
【讨论】:
有人可以让它在 Django 1.9 - Python 3 上工作吗?提前致谢!【参考方案4】:功能正在问题#25409 中讨论。将对 URL 进行重大返工,并计划在 Django 1.10 版本中发布。
【讨论】:
似乎讨论无处可去:(同时我发现github.com/twidi/django-decorator-include【参考方案5】:你可以使用 decorate_url
看这里
http://github.com/vorujack/decorate_url
你可以通过pip安装它
pip install decorate_url
github上的示例展示
【讨论】:
【参考方案6】:我知道这是一个非常古老的问题,所以对于任何对此有疑问的人来说,现在有一个非常简单的解决方案。
通过pip install django-decorator-include
安装django-decorator-include
。
这里是如何使用它:
from django.contrib.auth.decorators import login_required
from decorator_include import decorator_include
urlpatterns = [
path(r'^private/', decorator_include(login_required, 'private')),
]
这是GitHub documentation的链接。
这里是Pypi.org的链接
【讨论】:
以上是关于是否可以使用 login_required 在 django url 中装饰 include(...)?的主要内容,如果未能解决你的问题,请参考以下文章
使用Django自带的登录访问限制login_required
使用 @login_required 时的 Django 缓存