django:从IntegrityError中恢复的正确方法

时间:2014-06-29 02:02:06

标签: python django postgresql transactions postgresql-9.1

IntegrityError恢复的正确方法是什么,或者在不使用手动事务控制的情况下使我的事务搞砸的任何其他错误?

在我的应用程序中,我遇到了我想从中恢复的IntegrityError的问题,这搞砸了以后的数据库活动,让我留下:

DatabaseError: current transaction is aborted, commands ignored until end of transaction block` 

忽略IntegrityErrors后的所有数据库活动。

这段代码应该重现我看到的错误

from django.db import transaction

try:
    MyModel.save() # Do a bad save that will raise IntegrityError
except IntegrityError:
    pass

MyModel.objects.all() # raises DatabaseError: current transaction is aborted, commands ignored until end of transaction block

根据the docs,从IntegrityError恢复的解决方案是回滚交易。但是以下代码会生成TransactionManagementError

from django.db import transaction

try:
    MyModel.save()
except IntegrityError:
    transaction.rollback() # raises TransactionManagementError: This code isn't under transaction management

MyModel.objects.all() # Should work

编辑:我对来自TransactionManagementError的消息感到困惑,因为如果在我的except我做了:

connection._cursor().connection.rollback()

而不是django transaction.rollback()MyModel.objects.all()成功,如果我的代码“不在事务管理之下”则没有意义。没有在事务管理下的代码(我假设它意味着它使用自动提交),也可能有跨越多个查询的事务。

编辑#2:我知道使用手动事务控制可以从这些错误中恢复,但我不能在没有手册的情况下恢复交易控制?我的理解是,如果我使用自动提交,每个事务应该只有一个写入,因此会影响以后的数据库活动。

编辑#3:这是几年之后,但在django 1.4(不确定更高版本)中,另一个问题是Model.objects.bulk_create()不尊重自动提交行为。

版本:

  • Django:1.4(TransactionMiddleWare 启用)
  • Python:2.7
  • Postgres:9.1

1 个答案:

答案 0 :(得分:3)

Django的默认提交模式是AutoCommit。为了进行回滚,您需要将代码包装在事务中。 [docs]

with transaction.commit_on_success():
    # Your code here. Errors will auto-rollback.

要获取数据库级自动提交,您需要在DATABASES设置字典中使用以下选项。

'OPTIONS': {'autocommit': True,}

或者,您可以使用显式保存点回滚到。 [docs]

@transaction.commit_manually
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid)
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)
      # open transaction now contains only a.save()

  transaction.commit()