0Day Forums
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]