Django 1.4.22自动提交默认开启时如何临时关闭以实现回滚?
解决Django 1.4.22中dry_run模式下回滚失效的问题
首先,我得拆解下你遇到的核心问题:Django 1.4.22默认autocommit=True且无法全局修改,你用了@transaction.commit_manually装饰器,但dry_run里的回滚操作没生效,导致测试用例失败。
问题根源分析
从你的代码来看,最大的可能是你使用的self.cursor没有纳入Django的事务管理体系。Django的transaction模块只能管控它自己创建的数据库连接(即django.db.connection),如果你的self.cursor是通过自定义数据库连接生成的,那transaction.rollback()根本影响不到这些操作,自然回滚失效。
另外,在Django 1.4中,@transaction.commit_manually装饰器确实会临时关闭autocommit,但前提是所有数据库操作都通过Django提供的cursor执行。
解决方案:改用Django官方的数据库连接
下面是修改后的代码,核心是替换自定义cursor为Django的connection.cursor(),并调整事务逻辑:
from django.db import connection, transaction import logging logger = logging.getLogger(__name__) @transaction.commit_manually def run(self, dry_run): try: assert self.database in self.validate_values.keys() assert self.table in self.validate_values.values() except AssertionError as e: logger.error("%s or %s is not available. \n Error: %s" % (self.database, self.table, e.message)) return # 使用Django官方的cursor,确保纳入事务管理 cursor = connection.cursor() cursor.execute(self.SELECT_PATTERN % self.PGDict) current_value = cursor.fetchall() if len(current_value) > 1: logger.error("You can only update one item at a time.\n Current value: %s" % current_value) return elif len(current_value) == 0: logger.error("No data.") return elif len(current_value) == 1: if dry_run: logger.warning("Data below will be deleted.\n") logger.info("Current value: %s" % current_value) try: # 执行删除操作(此时在事务中,不会自动提交) cursor.execute(self.DELETE_PATTERN % self.PGDict) # 验证删除结果 cursor.execute(self.SELECT_PATTERN % self.PGDict) deleted_check = cursor.fetchall() assert len(deleted_check) == 0 # 手动回滚事务,恢复数据 transaction.rollback() logger.info("Dry run successfully.") # 再次查询确认数据已恢复 cursor.execute(self.SELECT_PATTERN % self.PGDict) actual_value = cursor.fetchall() print(actual_value) return actual_value except AssertionError as e: # 出错时也要确保回滚事务 transaction.rollback() logger.error("Dry run failed. \n Error: %s" % e.message) return else: logger.warning("Data deleting...\n") cursor.execute(self.DELETE_PATTERN % self.PGDict) cursor.execute(self.SELECT_PATTERN % self.PGDict) current_value = cursor.fetchall() try: assert len(current_value) == 0 # 手动提交事务,确认删除 transaction.commit() logger.info("Done.") return len(current_value) except AssertionError as e: # 删除失败,回滚 transaction.rollback() logger.error("Deletion failed. \n Error: %s" % e.message) return else: logger.error("Value Error") return
关键修改点说明
- 替换cursor来源:用
connection.cursor()替代self.cursor,确保所有数据库操作都在Django的事务管控范围内。 - 明确事务操作:在非dry_run模式下,删除成功后必须手动调用
transaction.commit();无论成功失败,dry_run模式下都要调用transaction.rollback()。 - 移除savepoint:在Django 1.4的
commit_manually模式下,直接使用全局事务的rollback更可靠,savepoint反而可能增加复杂度。
测试用例验证
你的测试用例test_run_dry_run现在应该能正常通过了:执行dry_run后,数据会被回滚,查询结果依然是原始的123456,长度为1,符合断言条件。
内容的提问来源于stack exchange,提问作者FakeYG
相关产品推荐
相关产品推荐

