Est-il possible de décorer include (...) dans les URL django avec login requis?
J'ai quelques zones restreintes sur le site, pour lesquelles je voudrais spécifier login_required
décorateur. Cependant je voudrais le faire une fois par inclusion dans main urls.py, pas par url individuelle dans inclus urls.py
Donc au lieu de:
/private/urls.py:
(r'^profile/$', login_required(profile)),
Je ferais quelque chose dans le sens:
/urls.py
urlpatterns = patterns('',
...
(r'^private/', login_required(include('private'))),
)
Sauf que ça ne marche pas, malheureusement.
5 réponses
C'est faisable, et en fait je viens de trouver deux extraits de pour ce.
Solution # 1
Le premier extrait de cotton remplace RegexURLPattern
et RegexURLResolver
avec des implémentations personnalisées qui injectent un décorateur donné pendant l'appel 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
, Vous devez l'utiliser comme ceci:
urlpatterns = patterns('',
# ...
(r'^private/', decorated_includes(login_required, include(private.urls))),
)
(notez que le paramètre include
ne peut pas être une chaîne avec cette méthode.)
Solution # 2
Une autre solution par sjzabel , que j'ai terminée en utilisant moi-même, est appliqué à l'extérieur patterns
appelez donc il peut être utilisé avec des chaînes et a une syntaxe légèrement différente. L'idée est la même, cependant.
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
Appelez-le comme ceci:
urlpatterns = patterns('',
# ...
)
urlpatterns += required(
login_required,
patterns('',
(r'^private/', include('private.urls'))
)
)
Les deux fonctionnent bien mais je préfère la dernière syntaxe.
Une alternative:
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'))),
)
Une version légèrement plus complexe, qui prend en charge plusieurs décorateurs:
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)),
)
login_required
est destiné à envelopper view callable, not include (), et à regarder le code source:
Http://code.djangoproject.com/browser/django/tags/releases/1.1.1/django/conf/urls/defaults.py#L9
-- Je ne pense pas qu'il existe un moyen facile d'utiliser default (ou même custom) login_required
avec include() pour atteindre ce que vous voulez atteindre.
En écrivant ceci, je pense que l'approche raisonnable serait d'utiliser un "middleware requis par connexion", comme celui-ci: http://www.djangosnippets.org/snippets/1179 / et oubliez de décorer les urls dans urls.py.
Vous pouvez utiliser decorate_url
Voir ici
Http://github.com/vorujack/decorate_url
Vous pouvez l'installer par pip
pip install decorate_url
Exemple d'affichage sur github