【问题标题】:django: Proper way to recover from IntegrityErrordjango:从 IntegrityError 中恢复的正确方法
【发布时间】:2014-08-19 18:53:28
【问题描述】:

IntegrityError 或任何其他可能导致我的交易在不使用手动交易控制的情况下搞砸的错误中恢复的正确方法是什么?

在我的应用程序中,我遇到了我想从中恢复的 IntegrityErrors 问题,这会破坏以后的数据库活动,让我有:

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() 不尊重自动提交行为。 p>

版本:

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

【问题讨论】:

  • 您尝试过Savepoint rollback 的建议吗?文档不清楚,但您可能需要处于transaction.commit_manually 模式才能使transaction.rollback 工作。查看模式here
  • 我认为可能是这种情况,但最好不必对项目中的每次保存都使用手动事务控制
  • 保存点对我没有帮助,因为我使用的是默认的自动提交行为。

标签: python django postgresql transactions postgresql-9.1


【解决方案1】:

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()

【讨论】:

  • 感谢您的回答,但是,我不想在每次保存时都使用手动事务控制。不确定还有其他方法,但我仍然很困惑,如果我使用自动提交,为什么以后的数据库活动会受到先前保存失败的影响。
  • 我正在更新我的问题以指定如何在不使用手动事务控制的情况下恢复
  • 那么,答案可能是打开数据库级别的自动提交。 'OPTIONS': {'autocommit': True,}
  • 最后一件事:你知道为什么当我只使用 django 级别的自动提交时事务没有自动回滚错误吗?我可能会为此提出一个问题。
  • Django 自动提交!= Postgres 自动提交。也不会自动回滚。
猜你喜欢
  • 2018-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多