Is it possible to decorate include(...) in django urls with login_required? - Printable Version +- 0Day Forums (https://zeroday.vip) +-- Forum: Coding (https://zeroday.vip/Forum-Coding) +--- Forum: FrameWork (https://zeroday.vip/Forum-FrameWork) +---- Forum: Django (https://zeroday.vip/Forum-Django) +---- Thread: Is it possible to decorate include(...) in django urls with login_required? (/Thread-Is-it-possible-to-decorate-include-in-django-urls-with-login-required) |
Is it possible to decorate include(...) in django urls with login_required? - multidimensionality407101 - 08-02-2023 I have a few restricted areas on the site, for which I would like to specify `login_required` decorator. However I would like to do that once per inclusion in main urls.py, not per individual url in included urls.py So instead of: /private/urls.py: (r'^profile/$', login_required(profile)), I'd do something along the lines: /urls.py urlpatterns = patterns('', ... (r'^private/', login_required(include('private'))), ) Except that it doesn't work, unfortunately. RE: Is it possible to decorate include(...) in django urls with login_required? - eon49 - 08-02-2023 `login_required` is meant for wrapping view callable, not include(), and looking at source code: [To see links please register here] -- I don't think there is an easy way to use default (or even custom) `login_required` with include() to achieve what you want to achieve. Writing this, I think that the reasonable approach would be to use some "login required middleware", like this one: [To see links please register here] and forget about decorating urls in urls.py.RE: Is it possible to decorate include(...) in django urls with login_required? - unsupernaturalizejivjco - 08-02-2023 It is doable, and in fact I just found [two][1] [snippets][2] for this. ## Solution #1 The first snippet by [cotton][3] substitutes `RegexURLPattern` and `RegexURLResolver` with custom implementations that inject given decorator during `resolve` call. 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 You need to use it like this: urlpatterns = patterns('', # ... (r'^private/', decorated_includes(login_required, include(private.urls))), ) (Note that `include` parameter can't be a string with this method.) ## Solution #2 Another solution by [sjzabel][4], which I ended up using myself, is applied *outside* `patterns` call so it can be used with strings and has a slightly different syntax. The idea is the same, though. 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 Call it like this: urlpatterns = patterns('', # ... ) urlpatterns += required( login_required, patterns('', (r'^private/', include('private.urls')) ) ) Both work fine but I prefer the latter syntax. [1]: [To see links please register here] [2]:[To see links please register here] [3]:[To see links please register here] [4]:[To see links please register here] RE: Is it possible to decorate include(...) in django urls with login_required? - parthenocarpically299885 - 08-02-2023 An 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'))), ) A slightly more complex version, that supports multiple decorators: 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)), ) RE: Is it possible to decorate include(...) in django urls with login_required? - andron251948 - 08-02-2023 > Feature is being discussed in issue [#25409][1]. There will be major rework for URLs and is planned for Django 1.10 release. [1]: [To see links please register here] RE: Is it possible to decorate include(...) in django urls with login_required? - renomination4419 - 08-02-2023 you can use decorate_url see here [To see links please register here] you can install it by pip pip install decorate_url example show on github RE: Is it possible to decorate include(...) in django urls with login_required? - marquise836232 - 08-02-2023 I know this is a very old question so for anyone who is wondering about the same, there is a very simple solution now. Install `django-decorator-include` via `pip install django-decorator-include`. Here is how to use it: ```python from django.contrib.auth.decorators import login_required from decorator_include import decorator_include urlpatterns = [ path(r'^private/', decorator_include(login_required, 'private')), ] ``` Here is the link to the [GitHub documentation][1]. And here is the link to [Pypi.org][2] [1]: [To see links please register here] [2]:[To see links please register here] |