Django在save方法中回滚事务

5 投票
3 回答
6761 浏览
提问于 2025-04-16 00:48

我有一段代码,它重写了一个模型的保存方法:

@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 增加1,但如果在调用父类的保存方法时发生了任何异常,事务就会回滚(这意味着 self.qa.vote_down_count += 1 的操作不会被保存到数据库里)。

但实际上发生的情况是:即使在调用父类的保存方法时出现了 IntegrityError 异常,self.qa.vote_down_count += 1 的操作还是被保存到了数据库里。

大家有什么想法吗?

3 个回答

1

我觉得Mike DeSimone的回答是对的。

关于数据库,如果你使用的是MySQL,具体情况取决于你用的版本。可能你的数据库表使用的是MyISAM引擎,这种引擎不支持事务。

要检查这一点,你可以在MySQL的命令行中运行以下命令:

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

你可以把你的表改成InnoDB,并在MySQL的配置中把默认存储引擎设置为innodb。
(详细信息可以查看这里:http://parasjain.net/2010/06/08/how-to-switch-to-innodb-database-in-mysql/。)

这样之后,事务就应该可以正常工作了。虽然使用Postgres会更好,但如果你想用MySQL/InnoDB,那么可能需要一些额外的步骤来加载带有前向引用的测试数据(这个问题的修复已经在Django的最新版本中,另外我也把它移植到了Django 1.3.1,具体可以查看Django 1.3.1.1在Github上的链接)。

3

试着使用保存点。可以这样做:

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

为什么不直接这样做呢:

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

这是文档中提到的做法,虽然他们建议在你的视图函数里这样做,所以你可能不需要在save()方法上加@transaction.commit_manually,而是应该把它放在视图里。

撰写回答