Django 默认状况下是运行一个打开的事务,这个事务在 model 中引发了数据变更的内置函式被调用时,就会被自动提交。例如,若是你调用 model.save() 或 model.delete(),改动就会被自动提交。python
这与不少数据库中的自动提交事务的设置类似。只要你执行了写数据库的动做,Django 就会生成对应的 INSERT/UPDATE/DELETE 语句,而后执行 COMMIT。这其中并无暗含 ROLLBACK 回滚操做。web
这种方式适用于在 web 请求中处理事务的状况,经过 TransactionMiddleware 将 web 请求/ web 应答与事务进行绑定。sql
工做流程如此:当 Django 收到一个请求时,就会对某个事务进行处理。若是在生成应答的过程当中没有出现问题,Django 就会提交未结束的事务。若是视图函式产生了异常,Django 就会回滚未结束的事务。数据库
要激活这个特性,只要将 TransactionMiddleware 中间件添加到 MIDDLEWARE_CLASSES 设置便可:django
MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.CacheMiddleware', 'django.middleware.transaction.TransactionMiddleware', )
这个顺序是至关重要的。事务中间件不只仅只做用于视图函式,也对排在它后面的全部中间件起做用。若是你将 session 中间件放在事务中间件的后面,那么建立 session 也会变成事务的一部分。缓存
CacheMiddleware 是一个例外,事务不会对它起做用。这是由于缓存中间件使用它本身的数据库游标(其内部使用本身的数据库游标)。session
对于大多数人来讲,隐式的基于请求的事务工做得很是好。可是,若是你须要对如何管理事务作更细致的控制,那么你可使用 Python 装饰器来改变这种由视图函式处理事务的方式。less
注意spa
虽然下面的例子使用视图函式作为例子,但这些装饰器也能够做用于非视图函式。翻译
使用 autocommit 装饰器时,会忽略全局事务设置,将某个视图函式转换化 Django 默认的事务提交行为。
例如:
from django.db import transaction @transaction.autocommit def viewfunc(request): ....
在 viewfunc() 里,一旦你调用了 model.save(), model.delete(),或是任何其余会写数据库的方法,事务就会被马上提交。
使用 commit_on_success 装饰器时,会令某个函式中每一项工做完成后都使用一个单独的事务:
from django.db import transaction @transaction.commit_on_success def viewfunc(request): ....
若是这个函式成功返回,接下来 Django 就会提交函式中全部已完成的工做。若是函式抛出异常,Django 就会对事务进行回滚。
若是须要对事务进行全面控制,可使用 commit_manually 装饰器。它告诉 Django 你将全面接管这个事务。
若是你的视图修改了数据却没有 commit() 或 rollback(), Django 将抛出 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()
早期 Django 版本的用户请注意:
数据库 connection.commit() 和 connection.rollback() 方法(在 Djanog 0.91 以及更早版本中被称为 db.commit() 和 db.rollback() ) 已不在再在。它们已经被 transaction.commit() 和 transaction.rollback() 替换。
在 Django 的配置文件中将 DISABLE_TRANSACTION_MANAGEMENT 设为 True 就会禁用全部的事务管理。
若是你如法设置,那么 Django 就再也不自动提供任何的事务管理;中间件也再也不隐式地提交事务,并且你须要自行去管理事务。甚至于你还要为其余的中间件提交事务。
所以,若是你想使用你本身写的事务中间件或是你想作一些很是古怪的东西,那么如上所作确实是最佳选择。可是在大多数状况下,你最好仍是关闭默认的事务行为或去掉事务中间件,而后只修改必要的函式。
保存点是事务内的一个标记,用来确保你能回滚部分事务,而不是回滚整个事务。保存点对 PostgreSQL 8 和 Oracle 数据库是很是有用的。其余数据库也提供 savepoint 函式,可是却只是个空操做,不会执行任何操做。
若是你使用 Django 默认的autocommit 事务行为,那么保存点功能并非特别有用。可是若是你使用 commit_on_success 或 commit_manually,每一个打开的事务都积累了一系列数据库操做,等待提交或是回滚。若是你要求回滚,那么整个的事务都会被回滚。保存点提供了这样一种能力,它能操做回滚的细节,作部分回滚,而不是使用 transaction.rollback() 一次性所有回滚。
在事务对象中,保存点由三个方法控制:
建立一个新的保存点,它在事务标记一个点,代表当前是 "good" 状态。
返回保存点的 ID (sid).
更新保存点。它对自保存点被建立以后,事务中执行的全部操做进行保存;或是对上次提交以后的全部操做进行保存。
将事务回滚到最后一次提交时的保存点。
下面这些例子演示了保存点的用法:
from django.db import transaction @transaction.commit_manually def viewfunc(request): a.save() # open transaction now contains a.save() sid = transaction.savepoint() b.save() # open transaction now contains a.save() and b.save() if want_to_keep_b: transaction.savepoint_commit(sid) # open transaction still contains a.save() and b.save() else: transaction.savepoint_rollback(sid) # open transaction now contains only a.save() transaction.commit()
若是你正在使用 MySQL,那么你的表可能支持,也可能不支持事务;这取决于你的 MySQL 版本和表的类型(这里的表类型,就是指 "InnoDB" 或 "MyISAM")。 MySQL 的事务特性已经超出了本文所讨论的范围,若是您感兴趣,能够查看 information on MySQL transactions。
若是你的 MySQL 不支持事务,那么 Django 将在 auto-commit 模式中实现这个功能:调用任何语句都被马上被运行,而后提交。若是你的 MySQL 支持事务,Django 将使用上文中所提到的方法来处理事务。
在调用 PostgreSQL 标游时,若是抛出异常 (好比 IntegrityError),同个事务中的全部 SQL 子查询都会曗一样的错误 "当前事务被中断,查询将被忽视,直至事务结束 current transaction is aborted, queries ignored until end of transaction block"。只是简单地使用 save() 并不会在 PostgreSQL 中抛开异常,这一般出如今使用更先进的特性时,好比保存含有惟一字段的对象,或保存时使用了 force_insert/force_update 标识,或是使用自定义 SQL 。
下面几种方法能够避免这种错误。
第一种方法就是回滚整个事务。例如:
a.save() # Succeeds, but may be undone by transaction rollback try: b.save() # Could throw exception except IntegrityError: transaction.rollback() c.save() # Succeeds, but a.save() may have been undone
调用 transaction.rollback() 回滚整个事务。任何未提交的数据库操做都会丢失。在这个例子中, a.save() 所作的修改将丢失,并且不会抛出任何异常。
若是你正在使用 PostgreSQL 8 或是更新版本,你可使用 savepoints 来控制回滚范围。在执行一个可能会失败的数据库操做以前,你能够设置或是更新保存点。若是这个操做失败了,你可回滚到上一个提交的操做,而不是回滚整个事务。例如:
a.save() # Succeeds, and never undone by savepoint rollback try: sid = transaction.savepoint() b.save() # Could throw exception transaction.savepoint_commit(sid) except IntegrityError: transaction.savepoint_rollback(sid) c.save() # Succeeds, and a.save() is never undone
在这个例子中,a.save() 将不会被回滚,而 b.save() 会抛出异常。
在使用 PostgreSQL 8.2 或更新的版本时,可使用 PostgreSQL 自带的一个先进特性 database-level autocommit。若是使用了这个特性,就不会出现打开而未提交的事务,因此即便使用了异常捕牛,它仍会继续运行。例如:
a.save() # succeeds try: b.save() # Could throw exception except IntegrityError: pass c.save() # succeeds
一般是比较复杂的SQL,用ORM 处理不方便的时候用的。或者是大批量SQL语句执行,比较在乎效率的状况下用。
首先说一下第二种状况,由于这种状况相对简单一点,没ORM 那么多东西,用我写的一个方法来解释
from django.db import connection, transaction ..... def batch_execsql(sqlarray): cursor = connection.cursor() # 获得处理的游标对象 ret="" try: for sql in sqlarray: cursor.execute(sql) transaction.commit_unless_managed() # 这是重点,没有这条语句,就不会commit 。 except Exception,e: #简单的异常处理,能够忽略 ret=str(e) cursor.close() return ret #有异常则返回异常,不然返回为空字符串
由上面能够看出 transaction.commit_unless_managed()的重要性,这是自定义SQL 语句状况下处理事务的方法. 上面的例子中的 sqlarray 表示一个list,里面有不少本身写的SQL 语句,而这些语句要求在一个事务中完成。
再来看看第一种状况,用ORM 的时候,事务的处理. 这在django 的官方文档中有说明,下面简单翻译介绍下
1. django 默认的事务, 是自动处理的,当你在调用 ORM 的model.save(),model.delete()的时候,全部改动会被当即提交的,至关于数据库设置了auto commit,没有隐藏的rollback.
2.对http请求的事务拦截,这是推荐的方式,使用了transaction中间件来完成,这是比较好的方法,但必须在settings.py中配置.
MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.transaction.TransactionMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', )
但须要注意的是,这样配置以后,与你中间件的配置顺序是有很大关系的。在 TransactionMiddleware 以后的全部中间件都会受到事务的控制。因此当你把session 中间件放到 Transaction以后,一样会受到影响。但 CacheMiddleware, UpdateCacheMiddleware, and FetchFromCacheMiddleware 不会受到影响,cache机制有本身的处理方式,用了内部的connection来处理
另外 TransactionMiddleware 只对 default 的数据库配置有效,若是要对另外的数据链接用这种方式,必须本身实现中间件。
这种状况下,你本身灵活控制事务.在settings.py 中不用配置 TransactionMiddleware 中间件了, 基本采用装饰模式来实现。
a)@transaction.autocommit ,django默认的事务处理, 采用此装饰模式会忽略掉全局的transaction 设置
from django.db import transaction @transaction.autocommit def viewfunc(request): .... @transaction.autocommit(using="my_other_database") def viewfunc2(request): ....
b) @transaction.commit_on_success 在一个方法中,全部工做完成后,提交事务。
from django.db import transaction @transaction.commit_on_success def viewfunc(request): .... @transaction.commit_on_success(using="my_other_database") def viewfunc2(request): ....
c) commit_manually() ,彻底本身处理,但若是你没有调用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() @transaction.commit_manually(using="my_other_database") def viewfunc2(request): ....