Управляемый блок транзакции закончился ожиданием COMMIT/ROLLBACK
У меня есть функция просмотра:
@transaction.commit_manually
def xyz(request):
if ABC:
success = something()
if success:
status = "success"
transaction.commit()
else:
status = "dataerrors"
transaction.rollback()
else:
status = "uploadproblem"
transaction.rollback()
return render(request, "template.html", {
'status': status,
})
Я считаю, что каждый путь кода заканчивает транзакцию так или иначе. Но Django, похоже, жалуется, что это не так. Любые идеи?
Django Version: 1.3
Exception Type: TransactionManagementError
Exception Value: Transaction managed block ended with pending COMMIT/ROLLBACK
РЕДАКТИРОВАТЬ: Никаких других исключений не требуется для изменения пути кода.
Ответы
Ответ 1
После получения аналогичной проблемы и траты времени на нее я понял, как отлаживать эту ситуацию.
По какой-то причине декоратор @transaction.commit_manually отключает исключения, которые происходят в функции.
Временно удалите декоратор из вашей функции, теперь вы увидите исключение, исправьте его и вернете декоратор!
Ответ 2
У меня была та же проблема. Единственное решение, которое я нашел, это использовать предложение try/finally, чтобы обеспечить фиксацию после рендеринга.
@transaction.commit_manually
def xyz(request):
committed = False
try:
if ABC:
success = something()
if success:
status = "success"
transaction.commit()
committed = True
else:
status = "dataerrors"
transaction.rollback()
committed = True
else:
status = "uploadproblem"
transaction.rollback()
committed = True
return render(request, "template.html", {
'status': status,
})
finally:
if not committed:
transaction.rollback() # or .commit() depending on your error-handling logic
Не имеет смысла, но это сработало для меня.
Ответ 3
У меня была одна и та же проблема, и я узнал, что даже если вы правильно закрываете транзакцию вручную в случае исключений, если вы затем снова напишите в orm в рамках области транзакций вручную, она как бы возобновит транзакцию и приведет к транзакции исключение.
with transaction.commit_manually():
try:
<exciting stuff>
transaction.commit()
except Exception, e:
transaction.rollback()
o.error='failed' <== caused transaction exception
Ответ 4
Другая причина, по которой вы можете столкнуться с этой проблемой, - это когда у вас в системе несколько БД.
Мне удалось преодолеть эту ошибку с помощью
@transaction.commit_manually(using='my_other_db')
def foo():
try:
<db query>
transaction.commit(using='my_other_db')
except:
transaction.rollback(using='my_other_db')
Ответ 5
Это всегда происходит, когда необработанное исключение происходит где-то в коде. В моем случае по какой-то причине исключение не было отправлено в отладчик, что и послужило причиной для меня путаницы.
Ответ 6
У меня была аналогичная проблема, возможно, этот код отлично подходит для вас:
@transaction.commit_on_success
def xyz(request):
if ABC:
success = something()
if success:
status = "success"
else:
status = "dataerrors"
transaction.rollback()
else:
status = "uploadproblem"
transaction.rollback()
return render(request, "template.html", {
'status': status,
})
Ответ 7
Как говорили другие люди, исключения, возникающие в декорированной функции, "теряются", потому что они перезаписываются исключением 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)
Ответ 8
У меня была одна и та же проблема, и я пробовал разные подходы. Вот что сработало для меня, но я не уверен, что это правильный способ сделать это. Измените свой оператор return на:
with transaction.commit_on_success():
return render(request, "template.html", {
'status': status,
})
Django Pros, это правильный подход?
Ответ 9
Поместите свой код в try/except block. За исключением блока просто выполните транзакцию и зарегистрируйте объект исключения.
@transaction.commit_manually
def xyz(xyz):
try:
some_logic
transaction.commit()
except Exception,e:
transaction.rollback()
print str(e)