0

So I'm able to add a decorator to several functions within my code, but I'm unsure as to how to add it to a class.

Here are my decorators:

def unauthenticated_user(view_func):
    def wrapper_func(request, *args, **kwargs):
        if request.user.is_authenticated:
            return redirect('index')
        else:
            return view_func(request, *args, **kwargs)
    return wrapper_func
def allowed_users(allowed_roles=[]):
    def decorator(view_func):
        def wrapper_func(request, *args, **kwargs):
            group = None
            if request.user.groups.exists():
                group = request.user.groups.all()[0].name
            
            if group in allowed_roles:    
                return view_func(request, *args, **kwargs)
            else:
                return HttpResponse('You are not authorised to view this page')
        return wrapper_func
    return decorator

And here is the class:

class PasswordsChangeView(PasswordChangeView):
    form_class = PasswordChangedForm
    success_url = reverse_lazy('password-success')
accdias
  • 5,160
  • 3
  • 19
  • 31
CandyAMG
  • 9
  • 2
  • Usually you use a mixin, not a decorator. You can use the `@method_decorator` to decorate a CBV, but usually it is not very elegant. – Willem Van Onsem Apr 11 '22 at 19:02
  • 1
    You don't add decorators *to* a class or function; you *apply* them. The semantics are simple: the decorator takes the class as an argument, and returns something that gets bound to the original name of the class. What the decorator *does* with the class is what you have to figure out, and you can't necessarily apply a decorator designed to be applied to a *function* to a class. – chepner Apr 11 '22 at 19:07

1 Answers1

1

While it is possible to work with decorators that work with functions with the @method_decorator(…) [Django-doc], usually one writes mixins.

For example for your unauthenticated_user decorator, you can write a mixin:

class UnauthenticatedUserMixin:
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated:
            return redirect('index')
        else:
            return super().dispatch(request, *args, **kwargs)

and then mix this into your view:

class MyView(UnauthenticatedUserMixin, TemplateView):
    # …

or for allowed_users:

AllowedUsersMixin:
    allowed_roles = ()
    
    def dispatch(self, request, *args, **kwargs):
        if request.user.groups.filter(name__in=self.allowed_roles).exists():
            return super().dispatch(request, *args, **kwargs)
        else:
            HttpResponse('You are not authorised to view this page')

and then use the mixin with:

class PasswordsChangeView(AllowedUsersMixin, PasswordChangeView):
    allowed_roles = ('group1', 'group2')
    form_class = PasswordChangedForm
    success_url = reverse_lazy('password-success')
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Hi, thank you this worked. How would I go about doing the same for the other decorator I included, I can't seem to figure out how I would go about writing a mixin for it. – CandyAMG Apr 11 '22 at 22:39