设为首页 收藏本站
查看: 760|回复: 0

[经验分享] Python开发入门与实战10-事务

[复制链接]

尚未签到

发表于 2015-11-29 10:14:33 | 显示全部楼层 |阅读模式
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))
  我们现在新增一条入库单来测试我们的入库事务是否实现了更新库存信息。
DSC0000.png
  提交成功后,我们查看数据库会发现库存表和入库单表数据都保存成功了,如下图:
DSC0001.png
DSC0002.png

1.1. 事务失败
  我们数据库inventory_instockbill表中增加一个非空字段remark来模拟,来模拟更新库存后,系统在提交入库单据时出现了异常系统返回失败了,由于model没有同步这个字段,入库单model提交时会引发错误,这时根据事务的规则,库存的更新应该会回滚,就是库存表数据不更新,入库单表没有新的入库单数据。如下图:
DSC0003.png
  运行的结果,库存表库存数量更新为25,但是入库单据没有保存成功,也就是意味着系统运行的结果与业务事务是不符合的,丢失的入库单据已经导致库存数量发生变化,我们需要用一定的机制来保证业务事务满足要求。如下图:
DSC0004.png
  

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. 事务测试
  现在我们重新执行前面事务失败的例子,来看系统运行的结果是否满足事务的基本要求。
DSC0005.png
  提交后系统报错,这我们也会发现数据库intentory表,提交的数据回滚了,库存数量没有更新。入库单业务实现了正确的业务事务,避免错误、混乱的数据提交到数据库中。

1.5. 小结
  本章节我们用入库的业务例子来阐述如何运行Django的事务机制,以满足业务事务的要求,例子中我们采用了commit_on_success(),在实际应用中可以根据自己的业务逻辑采用不同的事务标识。
  下一个章我们将继续以入库单的例子参数如何编写支持单元测试的代码例子。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-144826-1-1.html 上篇帖子: 【译】使用 Python 编写虚拟机解释器 下篇帖子: python进程池剖析(三)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表