在 Django 中出现异常时回滚一组数据库事务



首先,这个GitHub项目包含了我在这篇文章中讨论和询问的代码。这是一个非专有的简短示例,说明我的团队实际上正在研究的东西。

我正在做一个项目,我们使用 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()的特点,这可能吗?阅读文档时,它说:">如果代码块成功完成,则更改将提交到数据库。如果出现异常,则会回滚更改。我的问题是,是否可以将其扩展到在同一代码块中调用的其他操作回滚?

是的,在with atomic.transaction的同一块中,如果您调用raise Exception,您将回滚事务。

最新更新