Подтвердить что ты не робот

Можно ли украсить include (...) в django urls с login_required?

У меня есть несколько ограниченных областей на сайте, для которых я хотел бы указать login_required decorator. Однако я хотел бы сделать это один раз за включение в основной urls.py, а не по отдельному URL-адресу в include urls.py

Итак, вместо:

/private/urls.py:

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

Я бы сделал что-то в строю:

/urls.py

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

За исключением того, что это не работает, к сожалению.

4b9b3361

Ответ 1

Это выполнимо, и на самом деле я просто нашел два snippets для этого.

Решение № 1

Первый фрагмент cotton заменяет 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'))
    )
)

Оба работают нормально, но я предпочитаю последний синтаксис.

Ответ 2

Альтернатива:

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

Ответ 3

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/ и забыть об оформлении URL-адресов в urls.py.

Ответ 4

В статье рассматривается вопрос # 25409. Будет существенная переработка для URL-адресов и запланирована для выпуска Django 1.10.

Ответ 5

вы можете использовать decorate_url

см. здесь

http://github.com/vorujack/decorate_url

вы можете установить его на pip

pip install decorate_url

показать пример на github