
3.3 事 务
数据库事务是将在数据库上的许多操作(如读取数据库对象、写入、获取锁)封装成一个工作单元,在这个工作单元执行的过程中,所有操作要么都成功,要么都失败。数据库系统中的事务必须保持原子性、一致性、隔离性和持久性,也就是我们经常听到的ACID。了解和使用事务对于网站的开发是非常重要的,Django提供了一些控制数据库事务管理方式的方法。
3.3.1 事务管理
Django默认的模式是自动提交。除非事务处于活动状态(事务正在执行),否则每个查询都会立即提交到数据库。Django使用事务和保存点来保证需要多个查询的ORM操作的完整性,尤其是delete( )方法和update( )方法。
在处理Web事务时,一个常用的方法是将每个请求包装在事务中。可以在数据库的配置中设置ATOMIC_REQUESTS为True来开启这个功能。这个功能的工作流程如下:在调用视图函数之前,Django启动一个事务。如果生成的响应没有问题,则Django会提交事务;如果视图函数抛出了异常,则Django就回滚这个事务。
也可以使用atmoic( )上下文管理器,在视图函数中使用保存点执行子事务。在视图函数结束时,提交所有更改或不提交任何更改。atomic( )方法通常通过装饰一个视图函数来实现这一点。
需要注意的是,视图函数的执行是包含在事务中的,中间件和模板的渲染均在事务外运行。即使设置了ATOMIC_REQUESTS,也可以阻止在事务中运行视图函数,如下面的代码:

Django提供了API来控制事务。atomic( )方法允许在代码中保证数据库的原子性。如果代码执行成功,则将更改提交到数据库;如果代码抛出了异常,则回滚更改。代码示例如下:

需要提醒的是,开启事务会增加数据库的开销,应该尽可能使用短事务来减少此开销。
3.3.2 自动提交
在SQL标准中,事务通常通过把一批更改“积蓄”起来然后使之同时生效。在没有开启自动提交模式时,开发者使用事务必须使用语句BEGIN或者START TRANSACTION来显式开启一个事务,最后使用语句COMMIT来显式提交一个事务。
对于程序开发人员来说,这意味着每次查询都要加上提交语句,不是很方便。考虑到这一点,大多数数据库提供了自动提交模式。当打开自动提交模式且没有事务处于活动状态时,每个SQL查询都会包含在自己的事务中。换句话说,每个这样的查询不仅会启动事务,而且根据查询是否成功,事务也会自动提交或回滚。
根据Python数据库API规范,自动提交模式应该是默认禁止的。Django可设置配置默认禁止自动提交,修改settings.py文件,代码如下:
AUTOCOMMIT = False
进行以上设置后,Django将不会启用自动提交功能。开发者在代码中需要明确提交每个事务。
3.3.3 提交后执行操作
有些时候在数据库事务完成后需要执行其他逻辑,如发送电子邮件通知、清除缓存等,这时就可以使用on_commit( )方法来注册事务完成后的回调。示例代码如下:

on_commit( )方法经常和atomic( )方法一起使用,即在atomic( )方法代码块中注册回调函数,在事务成功后调用回调。示例代码如下:

在保存点回滚时,内层保存点注册的回调将不会调用。下面的代码先在外部设置一个保存点,开启一个新事务,然后在内层保存点范围内抛出一个异常,最后外层保存点注册的回调将会执行,而内层注册的回调不会被执行。
