Josseline Perdomo
Josseline Perdomo

Reputation: 383

Django - How to use decorator in class-based view methods?

I am developing a site with django 1.8. This is an example of one of the Views:

class ProfileView(View):
    template_name = 'index.html'

    # Return profile of any role (client/employee/admin)
    # Login required
    def get(self, request, *args, **kwargs):
        try:
            profile = Profile.objects.get(user=request.user)
            agency = None

            if request.user.is_employee():
                employee = EmployeeProfile.objects.get(profile=profile)
                agency = employee.agency

            if request.user.is_manager():
                agency = Agency.objects.get(manager=request.user)

        except (Profile.DoesNotExist, EmployeeProfile.DoesNotExist, Agency.DoesNotExist) as e:
            return HttpResponseRedirect('/404')

        return render(request, self.template_name, {"profile": profile, "agency": agency})

    # Client sign up
    # No decorator is needed
    def post(self, request):
        sign_up = SignUpForm(request.POST, request.FILES)

        response = json.loads(utils.create_user(sign_up,request.POST['avatar']))
        if response['profile'] is None:
            return JsonResponse({"code": 400, "response": response['message']})
        profile = serializers.deserialize("json", response['profile']).next().object

        group = Group.objects.get(name='clients')
        profile.user.groups.add(group)

        return HttpResponseRedirect('/')

The question is, can I have different decorators according to the type of request that is made to a controller (view)?

Upvotes: 20

Views: 35802

Answers (5)

TitanFighter
TitanFighter

Reputation: 5074

Since Django 1.9, as per the docs, it is possible to apply decorator in the next way:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

@method_decorator(login_required, name='dispatch')
class YourClassBasedView(TemplateView):
    ...

where name is the name of the method to be decorated.

or in case of few decorators:

from django.contrib.auth.decorators import login_required
from django.views.decorators.cache import never_cache
from django.utils.decorators import method_decorator

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class YourClassBasedView(TemplateView):
    ...

Upvotes: 38

Martín De la Fuente
Martín De la Fuente

Reputation: 6726

There is a very simple solution to achieve what you want, and it doesn't implies decorating dispatch method. You must use the method_decorator over your methods (get/post) and pass the decorator call (not the decorator itself) as a parameter.

In your case it would be:

from django.utils.decorators import method_decorator

class ProfileView(View):
    template_name = 'index.html'

    # Return profile of any role (client/employee/admin)
    @method_decorator(login_required())
    def get(self, request, *args, **kwargs):
        ...

    # Client sign up
    # No decorator is needed
    def post(self, request):
        ...

Note the parethesis in the login_required decorator.

You can pass any function-oriented decorator, even a custom one. For instance:

def white_list_check():
    def decorator(func):
        def wrapper(request, *args, **kwargs):
            ip = request.META.get('REMOTE_ADDR', '0.0.0.0')
            if ip in WHITE_LIST:
                return func(request, *args, **kwargs)
            else:
                return HttpResponseForbidden()
        return wrapper
    return decorator

And then, again:

class YourView(View):

    @method_decorator(white_list_check())
    def get(self, request):
        ...

Upvotes: 21

michel.iamit
michel.iamit

Reputation: 5906

Some decorators like never_cache can be used in the urls.py in stead of the old way: in views.py

e.g. the never_cache decorator:

in the old style views.py:

from django.views.decorators.cache import never_cache

@never_cache
def oldstyle_view(request):
    # and so on

when using class based views, in urls.py:

from django.views.decorators.cache import never_cache

urlpatterns = patterns('',
     (r'someurl/^$', never_cache(SomeClassBasedView.as_view())),
)

Edit 1st aug 2015

Note: this can be handy for those views where you do not have a complete view defined in views.py, otherwhise the decorator can be applied to the depatch method in the view as well.

Upvotes: 6

Animesh Sharma
Animesh Sharma

Reputation: 3386

You need to apply the decorator to the dispatch method of the class based view. This can be done as follows:

class ProfileView(View):

    @youdecorator
    def dispatch(self,request,*args,**kwargs):
        return super(ProfileView,self).dispatch(request,*args,**kwargs)
    //Rest of your code.

Upvotes: 4

Ozgur Vatansever
Ozgur Vatansever

Reputation: 52103

You can override dispatch method and call different decorators based on type of request:

from django.utils.decorators import method_decorator

class ProfileView(View):
    ... 

    def dispatch(self, *args, **kwargs):
        dispatch_method = super(ProfileView, self).dispatch

        if self.request.method == 'GET':
            dispatch_method = method_decorator(my_get_decorator)(dispatch_method)
        elif self.request.method == 'POST':
            dispatch_method = method_decorator(my_post_decorator)(dispatch_method)

        return dispatch_method(*args, **kwargs)

Upvotes: 5

Related Questions