1

While using the custom action method of ViewSet in Django, I separated some business logics into another class, of which name is BusinessService.

The BusinessService class can be used from many other methods, and after some analyses, I found all methods (more than 5) in the class should be in an atomic transaction.

So the easiest but repetitive way might be adding @transaction.atomic decorator above the name of the methods, but as a way to follow the DRY principle, I was struggling to remove the redundant repetitions, but couldn't make it in a simple way.

Is there any to make a whole class in an atomic transaction?


Till now, I tried attaching @transaction.atomic above the name of a class, but, of course, had no success, so I analyzed the decorator, and found out the Atomic class which uses __enter__ and __exit__ for transaction management, which requires with or additional something.

studroid
  • 13
  • 3

1 Answers1

0

You can try using a custom meta class (See What are metaclasses in Python?) which will decorate all methods of the created class. It will be something like below, (Note that there may be a few areas where this meta class can be refined):

from django.db import transaction


class AtomicTransactionMeta(type):
    def __new__(cls, name, bases, dct):
        for key, value in dct.items():
            if not key.startswith('__') and callable(value):
                # If a non-dunder method then decorate
                dct[key] = transaction.atomic(value)
        return super().__new__(cls, name, bases, dct)


class BusinessService(metaclass=AtomicTransactionMeta):
    ...
Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33
  • Holy Moly, I really appreciate you, @abdul-aziz-barkat!!! I didn't know metaclass can be used this way. I will go and try! – studroid May 14 '21 at 13:56
  • I found another approach here! : https://stackoverflow.com/a/64988399/15555320 ---- The above answer might be slightly ineffecitve in terms of performance because it iterates over all methods and adds the decorator logic instead of directly adds the logic to the called method. ---- In terms of the logic's scope, however, the above answer is exactly accurate because it works in the same scope as original decorator, but overriding __call__ method with `with` seems to somewhat extend the scope to the end of covering logics, so data is not inserted right after the end of the method. – studroid May 14 '21 at 14:46