【问题标题】:django rollback transaction in save method保存方法中的django回滚事务
【发布时间】:2010-07-04 12:44:14
【问题描述】:

我有以下代码覆盖模型的保存方法:

@transaction.commit_on_success
def save(self, *args, **kwargs):

    try:
        transaction.commit()
        self.qa.vote_down_count += 1
        self.qa.save()

        super(self.__class__, self).save(*args, **kwargs)

    except:
        transaction.rollback()
        raise
    else:
        transaction.commit()

预期的行为是:self.qa 属性 vote_down_count 加一,但如果在 super(self) 保存方法中发生任何异常,事务将回滚(这意味着 self.qa.vote_down_count += 1 未提交在数据库中)。

实际行为是:self.qa.vote_down_count += 1 已提交到数据库,即使从 super(self) 保存引发 IntegrityError 异常。

有什么想法吗?

【问题讨论】:

  • 你为什么先提交,最后再提交?
  • 提交第一件事会打开事务并确保提交之前的所有内容,不是吗?
  • 这在很大程度上取决于您使用的数据库。 Postgres 8.0+ 和 Oracle 正确支持事务。 MySQL 不能很好地使用它们,并且大多数其他数据库根本不支持它们。所以第一个问题是“你的数据库真的支持事务吗?”
  • @Gabriel,感谢您的回复。是的,它确实支持事务,我在 Postgresql 8.4 和 Sqlite 3 下测试了相同的代码。
  • 除了关于这个函数本身的风格问题(即忽略有更好的编写方法的事实),为什么如果 super() 失败,项目会保存到数据库中?

标签: python django


【解决方案1】:

为什么不干脆做:

@transaction.commit_manually
def save(self, *args, **kwargs):
    try:
        super(self.__class__, self).save(*args, **kwargs)
        self.qa.vote_down_count += 1
        self.qa.save()
    except:
        transaction.rollback()
        raise
    else:
        transaction.commit()

这就是the docs 的暗示方式,尽管他们说要在您的视图函数中执行此操作,因此您可能不需要save() 方法上的@transaction.commit_manually,而是将其放在视图上。

【讨论】:

  • 在我看来,改变操作的顺序并不能解决问题,也不能阻止错误的发生,还有其他情况必须先完成 self.qa.save() . commit_amually 装饰器不会改变行为。
  • 我有一个问题,你能帮我解决这个问题吗?你可以在这个链接上找到我的问题stackoverflow.com/questions/29574699/…@mike-desimone
【解决方案2】:

我认为 Mike DeSimone 的答案是正确的。

关于数据库,根据您使用的 MySQL 版本(如果您使用它),可能是您的数据库使用 MyISAM 引擎的表,不支持事务。

要检查它只需在 mysql shell 中运行:

SELECT TABLE_NAME, 
    ENGINE 
    FROM information_schema.TABLES 
    where TABLE_SCHEMA = 'your_db_name' ;

您可以在 MySQL 配置中将表更改为 InnoDB 并将 default_storage_engine 设置为 innodb。 (详情请看:http://parasjain.net/2010/06/08/how-to-switch-to-innodb-database-in-mysql/

之后事务应该可以工作了。最好使用 Postgres,但是如果你想使用 MySQL/InnoDB,那么可能你需要一个解决方法来加载带有前向引用的固定装置(BugFix 已经存在于 Django Trunk 中,我也将它移植到 Django 1.3.1,见Django 1.3.1.1 on Github)。

【讨论】:

    【解决方案3】:

    尝试使用savepoints。像这样的:

    def save(self, *args, **kwargs):
    
    try:
        sid = transaction.savepoint()
        self.qa.vote_down_count += 1
        self.qa.save()
    
        super(self.__class__, self).save(*args, **kwargs)
    
    except:
        transaction.rollback(sid)
        raise
    else:
        transaction.commit(sid)
    

    【讨论】:

    • 我猜你的意思是 savepoint_rollback 和 savepoint_commit,它也不起作用。
    猜你喜欢
    • 2016-04-16
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 2015-04-23
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 2020-09-14
    相关资源
    最近更新 更多