1

I have multiple views all inheriting from a base view. All views require the login_required decorator. I would like to add this as a method decorator to dispatch of the base view and then not have to add the decorator to every child view. I was not able to achieve this.

Is this generally not possible? What am I missing? What don't I know?

Here is a somewhat broken down version of my code:

class CoreView(CoreMixin,TemplateView):
    pass


class BaseView(CoreView):

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(BaseView, self).dispatch(request, *args, **kwargs)


class MyView(BaseView):

    def dispatch(self, request, *args, **kwargs):
        "Do Stuff"

I have tried to do my research but could not find an answer.

BTW: I am currently using django 1.8 and python 2.7.

jimfawkes
  • 357
  • 2
  • 12

3 Answers3

2

There is no 'direct' way, but you can find a few solutions in Python: Decorating a class method that is intended to be overwritten when inherited .

The only one which is simple to adapt for you would be to use a hook in dispatch, as in:

class CoreView(CoreMixin,TemplateView):
    pass


class BaseView(CoreView):

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        do_dispatch(self, request, *args, **kwargs)
        return super(BaseView, self).dispatch(request, *args, **kwargs)

    def do_dispatch(self, request, *args, **kwargs):
        # This is the method we'll override
        pass         


class MyView(BaseView):

    def do_dispatch(self, request, *args, **kwargs):
        "Do Stuff"
Thierry Lathuille
  • 23,663
  • 10
  • 44
  • 50
1

Your answer is in the documentation. For class-based views there are mixins. Also for permission required in case you miss it.

0

Try that:

def login_required_class(decorator):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)):
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate

@login_required_class(login_required)
class CoreView(CoreMixin,TemplateView):
    pass

@login_required_class(login_required)
class BaseView(CoreView):

    def dispatch(self, request, *args, **kwargs):
        return super(BaseView, self).dispatch(request, *args, **kwargs)

@login_required_class(login_required)
class MyView(BaseView):

    def dispatch(self, request, *args, **kwargs):
        "Do Stuff"
glegoux
  • 3,505
  • 15
  • 32
  • I was hoping to find a solution to minimize the number of duplicate decorators. This does not seem to solve that, since I would still be adding the decorator to every child view. – jimfawkes Jun 26 '17 at 21:53
  • `@login_required` expects the request as the first argument, but this adds the decorator to _every_ callable member of the class. This will surely produce an error on any of the other callables that do not take the request as the first argument. – knbk Jun 26 '17 at 23:06