Clash
Clash

Reputation: 5025

How to write a decorator for a class based view -- permision based on object from view

right now I'm using this app for permission checking: django-rules

However it hasn't been updated for over a year now and there's no decorator for the "new" (since django 1.3) class based views. I would like to be able to use at the urls.py like this:

url(r'^casos/(?P<pk>\d+)/editar/$', rules_permission_required('lawsuits.logical_check', raise_exception=True)(CaseUpdateView.as_view()), name='case_edit'),

I can't figure out how to get the object from the class based view from the decorator. Do you guys have any idea? Here's what I have so far:

from django.utils.decorators import available_attrs
def rules_permission_required(perm, queryset=None, login_url=None, raise_exception=False):
    def wrapper(view_func):
        @wraps(view_func, assigned=available_attrs(view_func))
        def inner(request, *args, **kwargs):
            #view_func is the class based view -> <function MyEditView at 0x94e54c4>

            print view_func.get_object() # doesnt work
            print view_func(request, *args, **kwargs).get_object() # doesnt work either

            #any ideas?

            if not request.user.has_perm(perm, obj=obj):
                return redirect_to_login(request, login_url, raise_exception)
            return view_func(request, *args, **kwargs)
        return inner
    return wrapper

Many thanks in advance!

Upvotes: 4

Views: 1763

Answers (2)

Clash
Clash

Reputation: 5025

I ended up using a class decorator

def rules_permission_required(perm, queryset=None, login_url=None, raise_exception=False):

    def wrapper(cls):                
        def view_wrapper(view_func):
            @wraps(view_func, assigned=available_attrs(view_func))
            def inner(self, request, *args, **kwargs):
                # get object
                obj = get_object_from_classbased_instance(
                        self, queryset, request, *args, **kwargs
                    )

                # do anything you want
            return inner
        cls.dispatch = view_wrapper(cls.dispatch)
        return cls
    return wrapper

Upvotes: 0

Danilo Bargen
Danilo Bargen

Reputation: 19432

Use method_decorator on the dispatch() method: https://docs.djangoproject.com/en/dev/topics/class-based-views/#decorating-class-based-views

from django.utils.decorators import method_decorator
class ClassBasedView(View):
    @method_decorator(rules_permission_required)
    def dispatch(self, *args, **kwargs):
        return super(ClassBasedView, self).dispatch(*args, **kwargs)

Or you could decorate the output of the as_view class method, either in your url config (as described in the link above), or by saving the instance into a variable.

class ClassBasedView(View):
    def dispatch(self, *args, **kwargs):
        return super(ClassBasedView, self).dispatch(*args, **kwargs)
class_based_view = rules_permission_required(ClassBasedView.as_view())

Though I'm not quite sure whether the last example could cause thread safety problems (depends on how Django handles the instances). The best way is probably to stick with the method_decorator.

Upvotes: 7

Related Questions