我有一个视图功能:
@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
编辑:没有其他异常会改变代码路径。
答案 0 :(得分:85)
在得到类似问题并浪费时间后,我想出了如何调试这种情况。
由于某种原因,@ transaction.commit_manually装饰器会使函数中出现异常。
暂时从你的函数中删除装饰器,你现在会看到异常,修复它并把装饰器放回去!
答案 1 :(得分:9)
我遇到了同样的问题。我找到的唯一解决方案是使用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
没有意义,但它对我有用。
答案 2 :(得分:2)
我遇到了同样的问题,并了解到即使您在异常的情况下手动正确关闭事务,如果您随后在手动事务范围内再次写入orm,它似乎以某种方式重新打开事务并导致事务异常。
with transaction.commit_manually():
try:
<exciting stuff>
transaction.commit()
except Exception, e:
transaction.rollback()
o.error='failed' <== caused transaction exception
答案 3 :(得分:2)
您可能会遇到此问题的另一个原因是系统中有多个数据库。
我能够用
克服这个错误@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')
答案 4 :(得分:1)
当代码中某处出现未处理的异常时,总会发生这种情况。在我的情况下,由于某种原因,调试器没有抛出异常,这是导致我混淆的原因。
答案 5 :(得分:1)
我有类似的问题,也许这段代码适合你:
@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,
})
答案 6 :(得分:1)
正如其他人所说,在装饰函数中发生的异常会“丢失”,因为它们会被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)
答案 7 :(得分:0)
我遇到了同样的问题并尝试了各种方法。这对我有用,但我不确定这是否是正确的方法。将您的退货声明更改为:
with transaction.commit_on_success():
return render(request, "template.html", {
'status': status,
})
Django Pros,这是正确的方法吗?
答案 8 :(得分:0)
将代码放在try / except块中。除了块之外,只需执行transaction.rollback并记录异常对象。
@transaction.commit_manually
def xyz(xyz):
try:
some_logic
transaction.commit()
except Exception,e:
transaction.rollback()
print str(e)
答案 9 :(得分:0)
根据需要进行数据库备份,并从数据库中删除表ROLLBACK_TEST
。
mysql> DROP TABLE `ROLLBACK_TEST`;