Python开发入门与实战10-事务

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(),在实际应用中可以根据自己的业务逻辑采用不同的事务标识。

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

时间: 2024-08-29 16:52:09

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

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

1.搭建Python Django开发环境 1.1.Python运行环境安装 Python官网:http://www.python.org/ Python最新源码,二进制文档,新闻资讯等可以在Python的官网查看到. Python3.0已经发布,本文我们使用Django作为对象映射层,Django暂时还不支持3.0版本,本文我们以Python 2.7 Windows 8 64位版本为例.下载安Windows X86-64 MSI Installer (2.7.7) [1]安装包,运行安装文件.

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

8. 基于Java的Python的集成开发环境 目前为止我们所有的代码和例子都是通过Notepad文本编辑器来实现的,实际项目开发中这种编码模式效率较低(大虾除外),使用IDE集成开发环境常常大幅度的提高编码效率.本章我们将简要介绍两个主流的集成开发环境. 8.1. 下载安装Java运行环境 http://www.java.com/zh_CN/download/manual.jsp 根据操作系统版本下载安装Java运行时环境,如下图: 8.2.Eclipse IDE http://www.ecl

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

17. 新浪云部署 上一章节我们介绍了如何在本地windows服务器部署python django的网站,本章我们简要说明一下如何把python django工程部署到云服务上. 本章章节我们描述如何在新浪云上部署python django 项目.首先,我们需要注册新浪云用户账号,新浪云用户现在需要实名认证,通过实名认证的账号创建的应用才能正常运行. 17.1. 创建应用 注册成功后登录SAE,进入我的首页,点击创建新应用,创建一个新的应用myseasite,开发语言选择Python. 17.2

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

12. Biz业务层 前面的章节我们把大量的业务函数都放在了views.py里,按照目前这一的写法,当我们编写的系统复杂较高时,我们的views.py将会越来越复杂,大量的业务函数包含其中使其成为一个包罗万象的文件.本章我们将阐述增加一个业务逻辑层来解决view层的复杂度,相当于在model层和view层中增加一个业务逻辑业务层Biz层,接下来我们根据这个思路来重构我们前面章节的代码. 12.1. 增加inventoryBiz类文件 在inventory app添加一个新的inventoryBi

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

6. 表单 从简朴的单个搜索框,到常见的Blog评论提交表单,再到复杂的自定义数据输入接口,HTML表单一直是交互性网站的重要交互手段.本章介绍如何用Django如何对用户通过表单提交的数据进行访问.有效性检查以及其它处理等. 首先,我们先简要介绍一下HttpRequest对象和Form对象. 6.1. 提交的数据信息 除了基本的元数据,HttpRequest对象有两个属性包含了用户所提交的信息: request.GET 和 request.POST.二者都是类字典对象,我们可以通过它们来访问G

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

7. Django Form 7.1. Form表单 Django带有一个form库,称为django.forms,这个库可以处理上一章提到的包括HTML表单的自动生成以及数据验证. 我们在inventory目录下增加forms.py文件,与models.py文件类似,专门用来存储form类. class ItemForm(forms.Form): ItemCode = forms.CharField() ItemName = forms.CharField() Remark = froms.C

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

2.第一个Django项目 上一章节我们完成了python,django和数据库等运行环境的安装,现在我们来创建第一个django project吧,迈出使用django开发应用的第一步. 2.1.创建第一个Django项目 我们创建一个我们存放Django的工作目录,示例:C:\My Files\Python Projects 在命令提示符窗口进入到刚才创建的目录,运行运行命令: django-admin.py startproject mysite 这样会在你的当前目录下创建一个目录mysi

Python开发入门与实战19-Windows Azure部署2

19. 微软云部署2 上一章节我们介绍了如何实现在微软云通过虚拟机部署我们的在python django应用,本章我们来介绍如何Windows Azure上部署通过部署网站的方式来部署我们的应用,这种部署方式更方便,与vs 2013的集成度更高. 19.1. 创建Web 应用 我们登陆Windows Azure中国账户后进入到管理门户,选择“web 应用”,点击新建按钮,如下图: 创建完成后,我们选择myazure进度到应用的管理界面,如下图: 点击“浏览”按钮,浏览该网站,如果网站创建成功浏览

Python开发入门与实战9-基于vs的集成开发环境

9. 基于visual studio的Python的集成开发环境 上一章我们描述了如何安装使用Java的集成开发环境Eclipse IDE,本章我们来说明另一种集成开发环境,也是笔者多年的开发习惯使用的环境,也由于这个原因,接下来的章节描述都将以本章说明的集成开发环境为例. 微软的Visual Studio系列:一种在国内使用非常广泛的集成开发环境.这里我们选择的版本是community 2013版本.自从Visual Studio 也有支持Python的开发插件后,笔者很快就又重新转移到Vis