Search code examples
pythondjangooopdecoratorpython-decorators

how to deal with python decorators in a class hierarchy?


I'm developing a Django project using DRF. I also have used drf-yasg for documentation purposes.

Long story short: I'm using class-based views and I have some APIs which are so similar and I decided to make a super-class and implement common parts of the APIs in it! For being more clear:

class MySuperApiView(APIView):
    permission_classes = [<some permission classes>]

    def uncommon(self):
        pass  # to be override in subclasses

    @swagger_auto_schema(request_body=request_body, responses=api_responses)
    def post(self, *args, **kwargs):
        # do some common stuff here
        self.uncommon()
        # do some other common stuff here

And I just override uncommon method in child-classes:

class SomeCustomApi(MySuperApiView):
    def uncommon(self):
        # do specific things here

It works fine but I have a little problem: Every Api have its own api_responses which is initialized in the swagger_auto_schema decorator in super-class! And it's not possible to change it!

What do you recommend for such a situation? I really want to do OOP and observe DRY principle.


Solution

  • I finally found the best way to do such a thing in Django! (So yes, I don't know how to deal with such a problem in other frameworks or languages!) I moved swagger_auto_schema decorator to child-class using a class decorator named method_decorator. So first of all I had to import this method:

    from django.utils.decorators import method_decorator
    

    And then I changed super-class and child-class like this:

    class MySuperApiView(APIView):
        permission_classes = [<some permission classes>]
    
        def uncommon(self):
            pass  # to be override in subclasses
    
        # <I removed decorator from here!>
        def post(self, *args, **kwargs):
            # do some common stuff here
            self.uncommon()
            # do some other common stuff here
    
    api_responses =  # responses which belong to "SomeCustomApi"
    
    @method_decorator(name='post',
                      decorator=swagger_auto_schema(request_body=request_body,
                                                    responses=api_responses))
    class SomeCustomApi(MySuperApiView):
        def uncommon(self):
            # do specific things here
    

    It works totaly fine :) however I prefer a solution in which I don't have to repeat the decorator and instead, just initialize the responses parameter. If you face such a problem in other languages and you have the answer for another language, please post your answer.