Using Multiple Decorators in urls.py in Django - django

I have an admin mixin that I'm using to prevent caching and make sure users are logged in by overriding dispatch(). It's being used in my class-based views.
# mixins.py
from django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
class AdminPageMixin(object):
#method_decorator(never_cache)
#method_decorator(staff_member_required)
def dispatch(self, request, *args, **kwargs):
return super(AdminPageMixin, self).dispatch(request, *args, **kwargs)
# views.py
class SomeAdminView(AdminPageMixin, ListView):
I'm running into a problem when I'm trying to run unit tests against SomeAdminView. Yes, I know I can use django's test client to login, but I'm trying to stay away from writing functional tests. I'd like, instead, to wrap AdminPageMixin functionality into a single decorator and call that decorator in urls.py, like so:
url(r'^myurl/$', decorator_wrapper(SomeAdminView.as_view()), name='some-admin-view'),
Alternatively, I could do this:
url(r'^myurl/$', never_cache(staff_member_required(SomeAdminView.as_view())), name='some-admin-view'),
but if I wanted to add a third or forth decorator, I'd be updating a lot of lines in urls.py and repeating a lot of code.
Any ideas how to create this decorator wrapper?

It is quite against the spirit of the CBVs to use decorators in the URLconfs. Instead, use mixins to add the functionality directly to the dispatch method of the view class.

Related

How to check if current url in list of urlpatterns?

I am a bit puzzled. I want to check if the current url of the website is in a specific subset of my urlpatters. Which function of django would do this? This is for a middleware that requests additional information before moving to a specific part of my website.
class AuthorityMiddleware(object):
def process_request(self, request):
current_url = request.path_info
if not request.user.is_anonymous() and current_url not in exclude_list:
if not request.session.get('authority'):
return get_the_authority(request)
How should exclude list relate to the urlpatterns defined in urls.py?
Ok, my issue basically is the result of bad design. The whole issue can be solved if I use decorators to ask for the authority for the views that require it. Back when I wrote the above code I was thinking that middlewares would be the only place to redirect and call further functions before a view is shown, but in fact they are good to apply a function to all your views. Decorators are better if the functions only apply to a subset of views.
from django.core.urlresolvers import reverse, resolve
from django.http import HttpResponseRedirect
def project_required(func):
def decorated(request, *args, **kwargs):
if not request.session.get('authority'):
request.session['previous_url'] = resolve(request.path_info).url_name
return HttpResponseRedirect(reverse('project_login'))
return func(request, *args, **kwargs)
return decorated

Django FormWizard and Permissions

I was wondering if there is a way to use permission decorators for Django's FormWizard. I know there is a way to do it via the urlconf, but I would like to avoid this and have my permissions all set up via the views.
I tried to override MyFormWizard.as_view() and add the decorators there, but then realised that as_view() is a #classonlymethod.
I don't have a lot of experience with Class Based Views and was wondering if there is an easy way to add the permission decorator on one of the FormWizard's methods? Any clues?
You don't have to decorate the view in the url conf. You can do so in your views.py,
protected_wizard_view = login_required(MyWizardView.as_view())
and then import protected_wizard_view in your urls.py.
(r'^wizard/', protected_wizard_view),
Another other option is to decorate the dispatch method, as described in the docs.
class MyWizardView(WizardView):
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(MyWizardView, self).dispatch(*args, **kwargs)
#Alasdair response is good, there's also a great app that contains many helpers and mixins that you can use for Class Based View, take a look here.
Run pip install django-braces and you can use LoginRequiredMixin
from braces.views import LoginRequiredMixin
class MyWizardView(LoginRequiredMixin, WizardView):
pass
There's also mixins like PermissionRequiredMixin, MultiplePermissionsRequiredMixin, GroupRequiredMixin ...

Having multiple decorators in Django and using only one

I am using django-tokenapi to allow for authentication of an Android project that is using Django as a backend. The project also has a web interface.
django-tokenapi uses the #token_required decorator to protect certain views. Django uses the #login_required decorator to protect certain views.
What I would like is to have only one view that is protected by #login_required OR #token_required so it can be used with either the webapp or Android app.
So, ideally, it would look like this:
#token_required
#login_required
def names_update(request):
....
....
However that does not work. Is there a better method of doing this? Or is the correct thing to have two views, one the webapp and one for Android, that are protected by the proper decorator, and then lead to the same method.
No there's no easy way, if possible at all, to write an generalized OR decorator according to you described.
However, you can write a new decorator that does exactly what you want:
from functools import wraps
from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt
def token_or_login_required(view_func):
"""
Decorator which ensures the user is either logged in or
has provided a correct user and token pair.
"""
#csrf_exempt
#wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
user = request.REQUEST.get('user')
if user and user.is_authenticated:
return view_func(request, *args, **kwargs)
token = request.REQUEST.get('token')
if user and token:
user = authenticate(pk=user, token=token)
if user:
login(request, user)
return view_func(request, *args, **kwargs)
return HttpResponseForbidden()
return _wrapped_view
this decorate combines both token_required and login_required decorators, and will allow access to the view either if the user is logged in, or the token is valid.
You could try assigning a new view variable to the old view:
#token_required
def names_update_token(request):
...
#login_required
names_update_login = names_update_token
This should have the effect of creating a second view named names_update_login which is just a pointer to the first so that the code remains the same.
EDIT:
Another thought, and one I have used, is to create a more "generic" view and call it from each of the other views:
def update_token(request):
...
#token_required
def names_update_token(request):
update_token(request)
#login_required
def names_update_login(request):
update_token(request)
This gets around the issue you mentioned while still maintaining only a single copy of the actual code that implements the view.
It is possible to use more than one decorator on a single view. But in this case I think you should seperate the view and apply the decorators seperately. Otherwise token_required decorator will try to authenticate using token which won't be available on a request made using browser.

Django Rest Framework: 'function' object has no attribute 'as_view'

I've been trying for a while to get a ModelResource or a View working using the Django Rest Framework. I'm following the examples but the code in the examples is not working for me. Can anyone tell me why I might be getting this error.
views.py
# Create your views here.
from django.http import HttpResponse
from django.utils import simplejson
from django.core import serializers
from djangorestframework.views import View
from djangorestframework.response import Response
from djangorestframework import status
from interface.models import *
def TestView(View):
def get(self, request):
return Person.objects.all()
urls.py
from django.conf.urls.defaults import *
from djangorestframework.resources import ModelResource
from djangorestframework.views import ListOrCreateModelView, InstanceModelView, View
from interface.models import *
from interface.views import *
class PersonResource(ModelResource):
model = Person
ordering = ('LastName')
urlpatterns = patterns('',
url(r'^$', 'interface.views.index'),
url(r'^testview/$', TestView.as_view()),
url(r'^people/$', ListOrCreateModelView.as_view(resource=PersonResource)),
)
I'm now getting the error 'function' object has no attribute 'as_view'.
Since this is the #1 hit on google for this error message and there's a more subtle and probably common cause for it than the OPs, I'm posting this comment here.
This error can also be caused by using a standard view decorator on a class based view instead of on the __dispatch__ method within the view.
def TestView(View): should be class TestView(View):. As it stands, you define a function called TestView which takes an argument called View -- its body defines an inner function, then returns None.
To add to Tim Saylor point,
https://docs.djangoproject.com/en/dev/topics/class-based-views/intro/#id1
To decorate every instance of a class-based view, you need to decorate
the class definition itself. To do this you apply the decorator to the
dispatch() method of the class.
A method on a class isn’t quite the same as a standalone function, so
you can’t just apply a function decorator to the method – you need to
transform it into a method decorator first. The method_decorator
decorator transforms a function decorator into a method decorator so
that it can be used on an instance method. For example:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
I am also getting this error but in my case i solved it with following idea.
That error usually happens if you try to override a class. That sometimes happens if you copy&paste code and forget to change e.g. the class name. But in my case it was little different
If you apply #login_required to a class, you will receive the error message:
‘function’ object has no attribute ‘as_view’
So, how should you decorate classes in Django now?
For class-based views, you have two options of decorating your classes.
1) Decorating the URLconf
2) Decorating the class
Both options leads to the same result - restricting the access to a class only for logged users. The difference between the options is how the decorator is applied to the class instance.Refer this page for decorators implementation
https://docs.djangoproject.com/en/1.4/topics/class-based-views/#decorating-class-based-views
use this if you use a class your decorator should be
import first:
from django.utils.decorators import method_decorator
then
#method_decorator(login_required(login_url='login'),name="dispatch")
class YourClassView(YourView):

How to specify that login is required somewhere other than a view in Django?

So I have an app called stats that lets me query my database in various ways and return information in a JSON format so I can have a nice ajaxy dashboard for graphing and visualizing. I'd like this app to be as reusable as possible, naturally, so I don't want to necesarily use the #login_required decorator on its views. In my case, however, I do want a login to be required before viewing any of the apps views. Is there a way to do this somewhere other than the views?
Perhaps something like this in my site's urls.py? (I know this won't work, an example of what I'm looking for)
urlpatterns = patterns('',
(r'^stat/', include('stats.urls'), login_required),
)
You can apply decorator for individual urls in urls.py in this manner:
from django.contrib.auth.decorators import login_required
import views
(r'^stat/', login_required(views.index))
you can use a middleware for that
here is example snippet - http://www.djangosnippets.org/snippets/1179/
you can use this snippet and define LOGIN_EXEMPT_URLS in your settings or modifiy it a little bit for your case
If you're concerned about reusability, rather than using login_required, you could use a decorator which requires login if a particular argument is passed to the view (which might default to a value of True). Off the top of my head, it might look a little like this:
from django.contrib.auth.decorators import login_required
def login_possibly_required(view_func):
def inner(request, *args, **kwargs):
try:
require_login = kwargs.pop('require_login')
if require_login:
return login_required(view_func)(request, *args, **kwargs)
except KeyError:
pass
return view_func(request, *args, **kwargs)
return inner
Then you'd define your views like so:
#login_possibly_required
my_view(request, arg1, arg2, require_login=True):
pass
Not tested, but you get the idea.