Django在save方法中回滚事务
我有一段代码,它重写了一个模型的保存方法:
@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 个回答
我觉得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上的链接)。
试着使用保存点。可以这样做:
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)
为什么不直接这样做呢:
@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
,而是应该把它放在视图里。