一尘不染

是否可以用login_required装饰Django URL中的include(…)?

django

我在网站上有一些限制区域,我想为此指定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'))),
                      )

不幸的是,除了它不起作用。


阅读 572

收藏
2020-03-30

共1个答案

一尘不染

这是可行的,实际上我为此找到了两个 片段。

解决方案1
棉花替代品的第一个片段,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的另一个解决方案(我最终使用了我自己)在外部 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'))
    )
)

两者都能正常工作,但我更喜欢后一种语法。

2020-03-30