首先,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