首先,this GitHub project包含我在本文中讨论和询问的代码。这是一个非专有和简短的例子,我的团队正在处理这些事情。在Django出现异常时回滚一组数据库事务
我正在研究一个项目,我们使用Django数据库事务在表格上进行更新,以准备转换为可用于业务目的的XML。我们通过使用transaction.atomic()
上下文管理器来确定错误得到正确处理,从而修复了以前破坏的代码。每个UPDATE
语句都位于一个上下文管理器中,而上下文管理器又位于一个函数中。有三个在程序中调用另一个函数(bulk_set()
)内的这些功能(set_yes()
,set_no()
和broken_query()
):
models.py
from __future__ import unicode_literals
from django.db import connection, DatabaseError, transaction
import pandas as pd
from django.db import models
class TestTable(models.Model):
value1 = models.IntegerField()
value2 = models.IntegerField()
same = models.CharField(max_length=3, null=True)
def setup_table():
"""
sets up the basic table.
several rows will have matching values, some won't.
:return: None
"""
row1 = TestTable(value1=1, value2=1)
row1.save()
row2 = TestTable(value1=2, value2=1)
row2.save()
row3 = TestTable(value1=56, value2=1)
row3.save()
row4 = TestTable(value1=10, value2=10)
row4.save()
def set_yes():
"""
sets "same" column on rows with matching value1 and value2 columns to "yes"
:return: None
"""
query = '''
UPDATE db_app_testtable
SET same = 'yes'
WHERE value1 = value2
'''
try:
with transaction.atomic():
cursor = connection.cursor()
cursor.execute(query)
except DatabaseError as ex:
print "set_yes has error %s" % (ex)
raise
finally:
cursor.close()
print_table()
def set_no():
"""
sets "same" column on rows with differing value1 and value2 columns to "no"
:return: None
"""
query = '''
UPDATE db_app_testtable
SET same = 'no'
WHERE value1 != value2
'''
try:
with transaction.atomic():
cursor = connection.cursor()
cursor.execute(query)
except DatabaseError as ex:
print "set_no has error %s" % (ex)
raise
finally:
cursor.close()
print_table()
def broken_query():
"""
a function meant to break. there is no column named 'different', so this should cause
a DatabaseError to be thrown upon execution.
:return: None
"""
query = '''
UPDATE db_app_testtable
SET different = 'lol no'
WHERE value1 = value2
'''
try:
with transaction.atomic():
cursor = connection.cursor()
cursor.execute(query)
except DatabaseError as ex:
print "broken_query has error %s" % (ex)
raise
finally:
cursor.close()
...
def bulk_set():
try:
set_no()
set_yes()
broken_query()
except Exception as gen_ex:
print "Exception has occurred."
raise
正如你所看到的,broken_query()
将无法正常工作,这是通过设计。我们试图设计一个代码块,如果broken_query()
失败,它将回滚set_yes()
和set_no()
完成的操作,这是不可避免的。
鉴于django.db.transaction.atomic()
的功能,这可能吗?读取documentation时,它说:“如果代码块已成功完成,则更改将提交给数据库。如果发生异常,则更改将回滚。”“我的问题是,这可以扩展到使在同一代码块中调用的其他操作也会回滚?
了解,但原子性只能扩展到该事务。我想以某种方式使事务表现为一个集合,以便如果一个失败,它们全部失败并且全部回滚。 – nerdenator
所以你需要将所有的事务都放在原子块中。 – wololoooo
虽然我认为这不是建议,但它的工作原理。 – wololoooo