web-dev-qa-db-ja.com

login_requiredを使用してDjango urlsのinclude(...)を装飾することは可能ですか?

サイトにいくつかの制限された領域があり、そのためにlogin_requiredデコレータを指定したいと思います。ただし、インクルードされたurls.pyの個々のURLごとではなく、メインのurls.pyのインクルードごとに1回実行したいと思います。

したがって、代わりに:

/private/urls.py:

(r'^profile/$', login_required(profile)),

私は線に沿って何かをします:

/urls.py

urlpatterns = patterns('',
                      ...
                      (r'^private/', login_required(include('private'))),
                      )

残念ながら、それが機能しないことを除いて。

40
Art

それは実行可能であり、実際、私はこれについて twosnippets を見つけました。

解決策#1

cotton による最初のスニペットは、RegexURLPatternRegexURLResolverを、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 による別の解決策は、私が自分で使用することになりましたが、outsidepatterns呼び出しを適用して、次のことができるようにします。文字列で使用され、構文が少し異なります。ただし、考え方は同じです。

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'))
    )
)

どちらも正常に機能しますが、私は後者の構文を好みます。

39
Dan Abramov

別の方法:

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)),
)
6
Augusto Hack

機能は問題で議論されています #25409 。 URLの大幅な修正が行われ、Django 1.10リリースが予定されています。

2
Yeo

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

あなたはdecorate_urlを使うことができます

ここを参照してください

http://github.com/vorujack/decorate_url

あなたはピップでそれをインストールすることができます

pip install decorate_url

githubでのショーの例

2
vorujack

私はこれが非常に古い質問であることを知っているので、同じことについて疑問に思っている人のために、今非常に簡単な解決策があります。

インストールDjango-decorator-include via pip install 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ドキュメント へのリンクは次のとおりです。

そしてここに Pypi.org へのリンクがあります

0
Anatol