Django 事务小结

官方关于事务的说明

Django 默认的事务行为(Django’s default transaction behavior)

Django 默认情况下是运行一个打开的事务,这个事务在 model 中引起了数据变动的内置函式被调用时,就会被自动提交。例如,如果你调用 model.save() 或 model.delete(),改动就会被自动提交。

这与很多数据库中的自动提交事务的设置相似。只要你执行了写数据库的动作,Django 就会生成对应的 INSERT/UPDATE/DELETE 语句,然后执行 COMMIT。这其中并没有暗含 ROLLBACK 回滚操作。

将 HTTP 请求与事务进行绑定(Tying transactions to HTTP requests)

这种方式适用于在 web 请求中处理事务的情况,通过 TransactionMiddleware 将 web 请求/ web 应答与事务进行绑定。

工作流程如此:当 Django 收到一个请求时,就会对某个事务进行处理。如果在生成应答的过程中没有出现问题,Django 就会提交未结束的事务。如果视图函式产生了异常,Django 就会回滚未结束的事务。

要激活这个特性,只要将 TransactionMiddleware 中间件添加到 MIDDLEWARE_CLASSES 设置即可:

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.CacheMiddleware',
    'django.middleware.transaction.TransactionMiddleware',
)

这个顺序是相当重要的。事务中间件不仅仅只作用于视图函式,也对排在它后面的所有中间件起作用。如果你将 session 中间件放在事务中间件的后面,那么创建 session 也会变成事务的一部分。

CacheMiddleware 是一个例外,事务不会对它起作用。这是因为缓存中间件使用它自己的数据库游标(其内部使用自己的数据库游标)。

在视图中控制事务管理(Controlling transaction management in views)

对于大多数人来说,隐式的基于请求的事务工作得非常好。但是,如果你需要对如何管理事务做更细致的控制,那么你可以使用 Python 装饰器来改变这种由视图函式处理事务的方式。

注意

虽然下面的例子使用视图函式做为例子,但这些装饰器也可以作用于非视图函式。

django.db.transaction.autocommit

使用 autocommit 装饰器时,会忽略全局事务设置,将某个视图函式转换化 Django 默认的事务提交行为。

例如:

from django.db import transaction

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

在 viewfunc() 里,一旦你调用了 model.save(), model.delete(),或是任何其他会写数据库的方法,事务就会被立刻提交。

django.db.transaction.commit_on_success

使用 commit_on_success 装饰器时,会令某个函式中每一项工作完成后都使用一个单独的事务:

from django.db import transaction

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

如果这个函式成功返回,接下来 Django 就会提交函式中所有已完成的工作。如果函式抛出异常,Django 就会对事务进行回滚。

django.db.transaction.commit_manually

如果需要对事务进行全面控制,可以使用 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() 替换。

如何使全局事务管理无效(How to globally deactivate transaction management)

在 Django 的配置文件中将 DISABLE_TRANSACTION_MANAGEMENT 设为 True 就会禁用所有的事务管理。

如果你如法设置,那么 Django 就不再自动提供任何的事务管理;中间件也不再隐式地提交事务,而且你需要自行去管理事务。甚至于你还要为其他的中间件提交事务。

因此,如果你想使用你自己写的事务中间件或是你想做一些非常古怪的东西,那么如上所做确实是最佳选择。但是在大多数情况下,你最好还是关闭默认的事务行为或去掉事务中间件,然后只修改必要的函式。

保存点(Savepoints)

保存点是事务内的一个标记,用来确保你能回滚部分事务,而不是回滚整个事务。保存点对 PostgreSQL 8 和 Oracle 数据库是非常有用的。其他数据库也提供 savepoint 函式,但是却只是个空操作,不会执行任何操作。

如果你使用 Django 默认的autocommit 事务行为,那么保存点功能并不是特别有用。但是如果你使用 commit_on_success 或 commit_manually,每个打开的事务都积累了一系列数据库操作,等待提交或是回滚。如果你要求回滚,那么整个的事务都会被回滚。保存点提供了这样一种能力,它能操作回滚的细节,做部分回滚,而不是使用 transaction.rollback() 一次性全部回滚。

在事务对象中,保存点由三个方法控制:

transaction.savepoint()

创建一个新的保存点,它在事务标记一个点,表明当前是 “good” 状态。

返回保存点的 ID (sid).

transaction.savepoint_commit(sid)

更新保存点。它对自保存点被创建之后,事务中执行的所有操作进行保存;或是对上次提交之后的所有操作进行保存。

transaction.savepoint_rollback(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 中的事务(Transactions in MySQL)

如果你正在使用 MySQL,那么你的表可能支持,也可能不支持事务;这取决于你的 MySQL 版本和表的类型(这里的表类型,就是指 “InnoDB” 或 “MyISAM”)。 MySQL 的事务特性已经超出了本文所讨论的范围,如果您感兴趣,可以查看 information on MySQL transactions。

如果你的 MySQL 不支持事务,那么 Django 将在 auto-commit 模式中实现这个功能:调用任何语句都被立刻被运行,然后提交。如果你的 MySQL 支持事务,Django 将使用上文中所提到的方法来处理事务。

在 PostgreSQL 事务中处理异常(Handling exceptions within PostgreSQL transactions)

在调用 PostgreSQL 标游时,如果抛出异常 (比如 IntegrityError),同个事务中的所有 SQL 子查询都会曗同样的错误 “当前事务被中断,查询将被忽视,直至事务结束 current transaction is aborted, queries ignored until end of transaction block”。只是简单地使用 save() 并不会在 PostgreSQL 中抛开异常,这通常出现在使用更先进的特性时,比如保存含有唯一字段的对象,或保存时使用了 force_insert/force_update 标识,或是使用自定义 SQL 。

下面几种方法可以避免这种错误。

事务回滚(Transaction rollback)

第一种方法就是回滚整个事务。例如:

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() 所做的修改将丢失,而且不会抛出任何异常。

保存点回滚(Savepoint rollback)

如果你正在使用 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() 会抛出异常。

数据库级的自动提交(Database-level autocommit)

在使用 PostgreSQL 8.2 或更新的版本时,可以使用 PostgreSQL 自带的一个先进特性 database-level autocommit。如果使用了这个特性,就不会出现打开而未提交的事务,所以即使使用了异常捕牛,它仍会继续运行。例如:

a.save() # succeeds
try:
    b.save() # Could throw exception
except IntegrityError:
    pass
c.save() # succeeds

Django进行事务管理

1.基于Django ORM的transaction处理

2.是基于自定义SQL 语句的transaction的处理

通常是比较复杂的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 的数据库配置有效,如果要对另外的数据连接用这种方式,必须自己实现中间件。

 

3.自己来控制事务

这种情况下,你自己灵活控制事务.在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):
    ....

 

    原文作者:数据库基础
    原文地址: https://my.oschina.net/u/2772739/blog/752533
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞