The problem -
@is_premium_user
def sample_view:
.......
......
I want certain views accesible to only the premium users of the website.
And how can I use this decorator across various applications in my project?
The problem -
@is_premium_user
def sample_view:
.......
......
I want certain views accesible to only the premium users of the website.
And how can I use this decorator across various applications in my project?
You don't have to write your own decorator for this as user_passes_test
is already included in Django.
And there's a snippet (group_required_decorator
) that extends this decorator and which should be pretty appropriate for your use case.
If you really want to write your own decorator then there's a lot of good documentation on the net.
And well, to (re-) use the decorator just put your decorator in a module on your path and you can import it from any other module.
Played around with the various links above and couldn't get them working and then came across this really simple one which I adapted. http://code.activestate.com/recipes/498217-custom-django-login_required-decorator/
from functools import wraps
from django.http import HttpResponseRedirect
def authors_only(function):
@wraps(function)
def wrap(request, *args, **kwargs):
profile = request.user.get_profile()
if profile.usertype == 'Author':
return function(request, *args, **kwargs)
else:
return HttpResponseRedirect('/')
return wrap
Using @wraps
is better than manually overriding like doing wrap.__doc__ = fn.__doc__
. Amongst other things, it ensures your wrapper function gets the same name as the wrapped function.
See https://docs.python.org/2/library/functools.html
Thanks to arie, the answer helped a long way, but it doesn't work for me.
When I found this snippet, I got it to work properly: http://djangosnippets.org/snippets/983/
This solution worked for me:
The helper function
This function has the benefit of being reusable in other places, as a drop in replacement for user.is_authenticated
. It could for instance be exposed as a template tag.
def my_custom_authenticated(user):
if user:
if user.is_authenticated():
return user.groups.filter(name=settings.MY_CUSTOM_GROUP_NAME).exists()
return False
The decorator
I just put this at the top of my views.py
, since it's so short.
def membership_required(fn=None):
decorator = user_passes_test(my_custom_authenticated)
if fn:
return decorator(fn)
return decorator
Using it
@membership_required
def some_view(request):
...
See examples in django itself:
http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/decorators.py
Your particular example is probably just a version of 'user_passes_test' where the test is going to be membership of the 'premium' group.
To use anywhere, make a python package and import it from there. As long as its on your sys.path it'll get found.
http://www.makina-corpus.org/blog/permission-required-decorator-django
i based mine off that blog post.
Stick that in a file in the python path or in a "util" app and import it into views:
e.g.
project_dir
|_ app1
|_ app2
|_ utils
|_ __init__.py
|_ permreq.py
from util.permreq import permission_required
@permmission_required('someapp.has_some_perm', template='denied.html')
def some_view(request):
blah blah
Here is a slightly different implementation, which allows additional parameters in order to specify which page to redirect to when validation fails, and which message to display to the end user:
from functools import wraps
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from core.helpers.flash import send_flash_error
def lender_only(redirect_to='plateforme.views.vue_login', error_flash_message=None):
def inner_render(fn):
@wraps(fn) # Ensure the wrapped function keeps the same name as the view
def wrapped(request, *args, **kwargs):
if request.context.user.is_authenticated and request.context.user.is_lender:
return fn(request, *args, **kwargs)
else:
if error_flash_message:
send_flash_error(request, error_flash_message) # Replace by your own implementation
return HttpResponseRedirect(reverse(redirect_to))
return wrapped
return inner_render
# Usage:
@lender_only('vitrine.views.projets', {'message': "Oops, can't go there."})
def render_page_index(request):
This guide helped me getting through it: https://elfsternberg.com/2009/11/20/python-decorators-with-arguments-with-bonus-django-goodness/ alongside the previous answers