1. 事务

本章我们将通过一个例子来简要的说明“事务”,这个开发实战里经常遇到的名词。事务是如何体现在一个具体的业务和系统的实现里。

事务是通过将一组相关操作组合为一个,要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。事务具有4个特性:原子性、一致性、隔离性、持久性。业务事务就是完成具体业务操作后,形成的业务结果;数据库事务是数据库产品根据事务的特性实现的相关功能,数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行,也可以理解成事务在数据库管理系统的实现。Django作为可以支持数据持久化的框架,也要支持的事务特性机制。

前面章节,我们描述的入库与库存的例子,现实世界的业务事务入库场景是这样,物品入库就是仓库管理员拿着新购进的物品,摆放到相应的货位上,同时更新库存登记簿,更新(增加)该物品的库存数据量,如果过程中仓库管理员忘记更新库存登记簿的库存数据将导致该物品的账目数据量与该物品的仓库货位实际库存数据量不一致,从而导致混乱。

库存管理业务系统依据这一业务逻辑规则,我们简化设计了2张表,入库单表和物品库存表来满足这一业务要求。业务系统的处理逻辑就是当新增的入库单数据保存到数据库入库表后,就必须更新库存表、该物品的库存数据,这个过程如果出现某种意外,如更新库存数据失败,就必须同时回滚、入库表的相应操作。这就是事务特性要求的:要么同时成功要么同时失败。

本章节将继续以入库事务这个例子来说明我们在系统中如何设计和实现事务的要求。

1.1. 入库操作流程

按照我们前面设计的入库单业务,每次入库单,其过程至少包括以下二个步数据库操作:

一、保存入库单信息到数据库;

二、更新入库单物品的当前库存信息,库存数量=当前库存数量+入库数量。

正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新物品库存信息时发生异常导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新物品库存信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态--库存信息没有被更新、入库单也没有提交成功。

1.1. 现在我们修改我们的代码来实现新增入库单时更新我们的库存信息

我们在AddInStockBill函数中增加如下代码来更新当前库存信息:

def AddInStockBill(request):
if request.method == 'POST':
form = InStockBillForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
inStockBill = InStockBill()
inStockBill.InStockBillCode = cd['InStockBillCode']
inStockBill.InStockDate = cd['InStockDate']
inStockBill.Amount = cd['Amount']
inStockBill.Operator = cd['Operator']
inStockBill.Item = cd['Item'] inventorys = inStockBill.Item.inventory_set.all()
currentInventory = Inventory()
if (inventorys.count()==0):
currentInventory.Item = inStockBill.Item
currentInventory.Amount = inStockBill.Amount
else:
#这里假定只有一个物料,后面我们会根据进程重构代码
currentInventory = inventorys[0]
currentInventory.Amount = currentInventory.Amount + inStockBill.Amount currentInventory.save() #更新库存
inStockBill.save() #保存入库单数据
            return HttpResponseRedirect('/success/')
else:
form = InStockBillForm() return render_to_response('InStockAdd.html',{'form': form}
,context_instance = RequestContext(request))

我们现在新增一条入库单来测试我们的入库事务是否实现了更新库存信息。

提交成功后,我们查看数据库会发现库存表和入库单表数据都保存成功了,如下图:

1.1. 事务失败

我们数据库inventory_instockbill表中增加一个非空字段remark来模拟,来模拟更新库存后,系统在提交入库单据时出现了异常系统返回失败了,由于model没有同步这个字段,入库单model提交时会引发错误,这时根据事务的规则,库存的更新应该会回滚,就是库存表数据不更新,入库单表没有新的入库单数据。如下图:

运行的结果,库存表库存数量更新为25,但是入库单据没有保存成功,也就是意味着系统运行的结果与业务事务是不符合的,丢失的入库单据已经导致库存数量发生变化,我们需要用一定的机制来保证业务事务满足要求。如下图:

1.2. Django事务处理

默认情况下,在Django中事务是自动提交的。当我们运行Django内置的模板修改函数时,例如调用model.save()或model.delete()时,事务将被立即提交。这种机制和数据库的自动提交事务机制类似。记住这里没有默认的回滚机制,要解决刚才的场景我们须引入Django的数据库事务控制类django.db.transaction

1.2.1. 在View中实现事务控制

如果想在更细粒度的条件下(例如在一个view函数中)控制事务,我们可以使用django.db.transaction。有两种用法:

1. 使用装饰器

from django.db import transaction

@transaction.commit_on_success
def viewfunc(request):
# ... # this code executes inside a transaction # ...

2. 使用context manager

from django.db import transaction

def viewfunc(request):
# ... # this code executes using default transaction management # ... with transaction.commit_on_success():
# ... # this code executes inside a transaction # ...
1.2.2. 标识使用方法

1. autocommit

使用autocommit装饰器可以将view函数中的事务还原成Django默认的自动提交模式,无视全局事务的设置。

from django.db import transaction 

@transaction.autocommit
def viewfunc(request): ....

2. commit_on_success()

顾名思义,view函数成功则提交事务,否则回滚。用法同上。

3. commit_manually()

告诉Django我们将自己控制函数中的事务处理。并且要注意,如果在视图函数中改变了数据库的数据并且没有调用commit()或rollback(),那么将抛出TransactionManagementError异常。

from django.db import transaction

@transaction.commit_manually
def viewfunc(request): ...
# You can commit/rollback however and whenever you want
transaction.commit()
... # But you've got to remember to do it yourself! try:
... except: transaction.rollback() else: transaction.commit()

1.3. AddInStockBill增加事务标识

from django.db import transaction

@transaction.commit_on_success
def AddInStockBill(request):

1.4. 事务测试

现在我们重新执行前面事务失败的例子,来看系统运行的结果是否满足事务的基本要求。

提交后系统报错,这我们也会发现数据库intentory表,提交的数据回滚了,库存数量没有更新。入库单业务实现了正确的业务事务,避免错误、混乱的数据提交到数据库中。

1.5. 小结

本章节我们用入库的业务例子来阐述如何运行Django的事务机制,以满足业务事务的要求,例子中我们采用了commit_on_success(),在实际应用中可以根据自己的业务逻辑采用不同的事务标识。

下一个章我们将继续以入库单的例子参数如何编写支持单元测试的代码例子。

Python开发入门与实战10-事务的更多相关文章

  1. Python开发入门与实战1-开发环境

    1.搭建Python Django开发环境 1.1.Python运行环境安装 Python官网:http://www.python.org/ Python最新源码,二进制文档,新闻资讯等可以在Pyth ...

  2. Python开发入门与实战8-基于Java的集成开发环境

    8. 基于Java的Python的集成开发环境 目前为止我们所有的代码和例子都是通过Notepad文本编辑器来实现的,实际项目开发中这种编码模式效率较低(大虾除外),使用IDE集成开发环境常常大幅度的 ...

  3. Python开发入门与实战5-django模型

    5.Django模型 在当今的Web 应用中,主观逻辑经常牵涉到与数据库的交互,数据库驱动网站.在后台连接数据库服务器,从中取出一些数据,然后在 Web 页面用各种各样的格式展示这些数据.这个网站也可 ...

  4. Python开发入门与实战17-新浪云部署

    17. 新浪云部署 上一章节我们介绍了如何在本地windows服务器部署python django的网站,本章我们简要说明一下如何把python django工程部署到云服务上. 本章章节我们描述如何 ...

  5. Python开发入门与实战13-基于模板的界面

    13. 基于模板的界面 本章我们将继续基于库存的简单例子来阐述如何在python django中体现MVC的架构,根据djangobook说明: M:数据存取部分,由django数据库层处理,本章要讲 ...

  6. Python开发入门与实战12-业务逻辑层

    12. Biz业务层 前面的章节我们把大量的业务函数都放在了views.py里,按照目前这一的写法,当我们编写的系统复杂较高时,我们的views.py将会越来越复杂,大量的业务函数包含其中使其成为一个 ...

  7. Python开发入门与实战7-Django Form

    7. Django Form 7.1. Form表单 Django带有一个form库,称为django.forms,这个库可以处理上一章提到的包括HTML表单的自动生成以及数据验证. 我们在inven ...

  8. Python开发入门与实战6-表单

    6. 表单 从简朴的单个搜索框,到常见的Blog评论提交表单,再到复杂的自定义数据输入接口,HTML表单一直是交互性网站的重要交互手段.本章介绍如何用Django如何对用户通过表单提交的数据进行访问. ...

  9. Python开发入门与实战2-第一个Django项目

    2.第一个Django项目 上一章节我们完成了python,django和数据库等运行环境的安装,现在我们来创建第一个django project吧,迈出使用django开发应用的第一步. 2.1.创 ...

随机推荐

  1. s3c2440 移值u-boot-2016.03 第3篇 支持Nor flash 识别

    当选择,NOR flash 启用时,才可以访问 NOR FLASH ./common/board_r.c 364 line:initr_flash()flash_size = flash_init() ...

  2. 芯航线FPGA学习套件之多通道串行ADDA(TLV1544,TLC5620)模块测试手册

    芯航线FPGA学习套件之多通道串行ADDA模块测试手册   本手册以简明扼要的方式介绍芯航线FPGA学习套件提供的ADDA模块的测试方法:   连接开发板,如下所示: 2.将ADDA V1.1模块与开 ...

  3. 注册、卸载DLL

    注册.卸载DLL,一般命令写在bat文件中,下面以注册.卸载SWFToImage.DLL为例. 1.注册文件(Install.bat)内容: REM copying files to the syst ...

  4. SpringMVC框架的基础知识;

    首先 在javaEE环境下,建立一个动态的web工程: 导入架包.... 建立一对多映射关系的封装类,这儿只写属性,getter和setter方法就不写了: 1: private String pro ...

  5. kibana安装与基础用法

    来自官网,版本为4.5 下载rpm包并安装 wget -c https://download.elastic.co/kibana/kibana/kibana-4.5.4-1.x86_64.rpm rp ...

  6. Python中的logging模块

    http://python.jobbole.com/86887/ 最近修改了项目里的logging相关功能,用到了python标准库里的logging模块,在此做一些记录.主要是从官方文档和stack ...

  7. JSP 中EL表达式用法详解

    EL 全名为Expression Language EL 语法很简单,它最大的特点就是使用上很方便.接下来介绍EL主要的语法结构: ${sessionScope.user.sex} 所有EL都是以${ ...

  8. ReentRantLock使用

    synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候. 1.某个线程在等待一 ...

  9. Spring Boot Admin的使用

    http://www.jianshu.com/p/e20a5f42a395 ******************************* 上一篇文章中了解了Spring Boot提供的监控接口,例如 ...

  10. 如何管理linux开机自启服务

    如何管理linux开机自启服务? 自启动服务非常重要,例如 (1)需要手动添加希望自启的服务,如安装svn后没有自动添加,就需要我们手动加入(2)安装某些程序后,自动加到自启动了,但我们不需要,需要手 ...