2017-08-11 44 views
1

首先,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时,它说:“如果代码块已成功完成,则更改将提交给数据库。如果发生异常,则更改将回滚。”“我的问题是,这可以扩展到使在同一代码块中调用的其他操作也会回滚?

回答

0

是的,在with atomic.transaction的同一个块中,如果您拨打raise Exception,您将使事务回滚。

+0

了解,但原子性只能扩展到该事务。我想以某种方式使事务表现为一个集合,以便如果一个失败,它们全部失败并且全部回滚。 – nerdenator

+0

所以你需要将所有的事务都放在原子块中。 – wololoooo

+0

虽然我认为这不是建议,但它的工作原理。 – wololoooo

相关问题