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

not*_*peg 6 python django postgresql transactions postgresql-9.1

什么是从一个IntegrityError或任何其他错误中恢复的正确方法,这些错误可能会导致我的交易失败而不使用手动事务控制?

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

DatabaseError: current transaction is aborted, commands ignored until end of transaction block` 
Run Code Online (Sandbox Code Playgroud)

对于忽略后的所有数据库活动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
Run Code Online (Sandbox Code Playgroud)

根据文档,从a恢复的解决方案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
Run Code Online (Sandbox Code Playgroud)

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

connection._cursor().connection.rollback()
Run Code Online (Sandbox Code Playgroud)

而不是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

Tho*_*mas 3

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

with transaction.commit_on_success():
    # Your code here. Errors will auto-rollback.
Run Code Online (Sandbox Code Playgroud)

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

'OPTIONS': {'autocommit': True,}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用显式保存点来回滚。[文档]

@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()
Run Code Online (Sandbox Code Playgroud)