Как говорили другие люди, исключения, возникающие в декорированной функции, "теряются", потому что они перезаписываются исключением TransactionManagementError
.
Предлагаю расширить transaction.commit_manually
декоратор. Мой декоратор transaction_commit_manually
использует декоратор transaction.commit_manually
внутри; если в оформленной функции возникает исключение, мой декоратор перехватывает исключение, выполняет transaction.rollback()
и снова вызывает исключение. Таким образом, транзакция очищается правильно, и исходное исключение не теряется.
def _create_decorator_transaction_commit_manually(using=None):
def deco(f):
def g(*args, **kwargs):
try:
out = f(*args, **kwargs)
except Exception as e:
if using is not None:
transaction.rollback(using=using)
else:
transaction.rollback()
raise e
return out
if using is not None:
return transaction.commit_manually(using=using)(g)
return transaction.commit_manually(g)
return deco
def transaction_commit_manually(*args, **kwargs):
"""
Improved transaction.commit_manually that does not hide exceptions.
If an exception occurs, rollback work and raise exception again
"""
# If 'using' keyword is provided, return a decorator
if 'using' in kwargs:
return _create_decorator_transaction_commit_manually(using=kwargs['using'])
# If 'using' keyword is not provided, act as a decorator:
# first argument is function to be decorated; return modified function
f = args[0]
deco = _create_decorator_transaction_commit_manually()
return deco(f)