This is a simple modification to the standard transaction.commit_on_success decorator that is aware of existing transactions. If a managed transaction is already active then the wrapped function is called directly, assuming the active transaction will clean up as appropriate. Otherwise, a standard commit_on_success is performed.
I'm using this to wrap the save() method of models that manipulate other related models on save, e.g:
@nested_commit_on_success def save(self): super(MyClass,self).save() for m in other_models: self.fix_up_other_model(m) m.save()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
def nested_commit_on_success(func): """Like commit_on_success, but doesn't commit existing transactions. This decorator is used to run a function within the scope of a database transaction, committing the transaction on success and rolling it back if an exception occurs. Unlike the standard transaction.commit_on_success decorator, this version first checks whether a transaction is already active. If so then it doesn't perform any commits or rollbacks, leaving that up to whoever is managing the active transaction. """ commit_on_success = transaction.commit_on_success(func) def _nested_commit_on_success(*args, **kwds): if transaction.is_managed(): return func(*args,**kwds) else: return commit_on_success(*args,**kwds) return transaction.wraps(func)(_nested_commit_on_success)