Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 460 Vote(s) - 3.42 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Is it possible to decorate include(...) in django urls with login_required?

#1
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.
Reply

#2
`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.

Reply

#3
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]

Reply

#4
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)),
)
Reply

#5
> 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]

Reply

#6
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
Reply

#7
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]

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through