Django之数据库事务

Django默认事务行为

  1. Django的默认使用自动提交模式。除非事务处于活动状态,否则每个查询都会立即提交到数据库。
  2. 为了确保ORM操作的完整性,Django自动使用事务或者保存点。
  3. TestCase由于性能原因,Django的类还会在事务中包装每个测试。

事务与HTTP请求

  1. 处理方式:将每个HTTP请求包装在事务中。
  2. 工作原理:在调用视图函数之前,Django启动一个事务。如果视图响应正常,Django会提交事务。如果视图产生异常,Django将回滚事务。
  3. 自定义事务:通过atomic()上下文管理器来使用视图代码中的保存点执行子事务。
  4. 结果:在视图结束时,将提交所有更改或不提交任何更改。

事务控制(atomic)

  • atomic(using = None,savepoint = True)
      using:数据库名称的参数。如果未提供此参数,Django将使用该"default" 数据库。
      savepoint :是否创建保存点。
  • atomic允许我们创建一个保证数据库原子性的代码块,如果代码块执行正常,则更改将提交到数据库。如果存在异常,则回滚更改。
  • atomic可以嵌套。在这种情况下,当内部块成功完成时,如果稍后在外部块中引发异常,则仍可以回滚其效果。
atomic 使用方式
  1. 作为装饰器使用
from django.db import transaction

@transaction.atomic
def viewfunc(request):
    do_stuff() # 该代码块在atomic事务中执行
  1. 作为上下文管理器使用
from django.db import transaction

def viewfunc(request):
    do_stuff()  # 该代码块在自动提交模式事务(Django默认)下执行

    with transaction.atomic():
        do_more_stuff()  # 该代码块在atomic事务中执行
  1. 上下文管理器嵌套使用
from django.db import transaction

def viewfunc(request):
    with transaction.atomic():
        with transaction.atomic():
            do_inside_stuff()  
        do_outside_stuff()  

  如果do_outside_stuff()执行异常,无论do_inside_stuff()是否正常执行,都将被回滚

  1. 嵌套在try / except块中
from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()

  由于try / except会捕获generate_relationships()引起的IntegrityError,所以即使generate_relationships()过破坏完整性约束导致数据库错误,create_parent()add_children()仍然能被正常执行。如果handle_exception()则意味着generate_relationships()已被安全回滚。

  • 为了保证原子性,atomic禁用一些API。试图在原子块中提交、回滚或更改数据库连接的自动提交状态将引发异常。
Django事务管理代码
  1. 进入最外面的atomic块时打开一个事务;
  2. 进入内部atomic块时创建保存点;
  3. 退出内部块时释放或回滚到保存点;
  4. 退出最外面的块时提交或回滚事务。
atomic(using = None,savepoint = True)
  • atomic接受一个using应该是数据库名称的参数。如果未提供此参数,Django将使用该"default" 数据库。
  • 通过将savepoint参数设置为False,可以禁用为内部块创建保存点。如果发生异常,Django将在使用保存点退出第一个父块时执行回滚(如果有保存点),否则执行最外层的块。原子性仍然由外部事务保证。只有当保存点的开销显著时,才应该使用此选项。它的缺点是破坏了上面描述的错误处理。
  • 当自动提交被关闭时,可以使用atomic。它只会使用保存点,即使对于最外层的块也是如此。

自动提交(Autocommit)

  • 为什么Django使用自动提交?
      在SQL标准中,每个SQL查询都启动一个事务(除非其中一个已经处于活动状态),并且必须显式地提交或回滚此类事务。
      对于应用程序开发人员来说,这并不总是很方便。为了缓解这个问题,大多数数据库都提供了自动提交模式。
  • 如何停用事务管理(取消自动提交)
      通过在配置(settings.py)中设置AUTOCOMMITFalse来完全禁用给定数据库的Django事务管理。
      如果禁用Django事务管理,则要求每个事务都必须被显式地提交,即使是Django或第三方库启动的事务。

事务提交后执行动作(on_comment)

  • on_comment(func, using=None)
      当事务提交成功后,调用任何不带参数的函数
from django.db import transaction

def do_ending():
    pass 
def do_something():
    pass

def function():
     transaction.on_commit(do_something)
    with transaction.atomic():  
        transaction.on_commit(do_ending)
  • 如果on_commit()在没有活动事务时调用,则将立即执行回调。所以do_something将被立即调用。
  • 如果on_commit()在有活动事务时调用,只有在成功提交事务后,才会执行回调。所以只有事务成功提交后,do_ending才会被on_commit()调用
  • 如果事务被回滚(在atomic()块中引发未处理异常时),函数将被丢弃,并且永远不会被调用。
  • 当使用嵌套atomic时,只有外部事务被成功提交后,on_commit()才会执行回调
with transaction.atomic():  # 外部事务
    transaction.on_commit(foo)

    with transaction.atomic():  # 内部事务
        transaction.on_commit(bar)
  • foo() 和 bar()只有在外部事务成功提交后,才会被.on_commit回调
  • 如果内部事务提交异常,foo() 和 bar()将被丢弃,并且永远不会被调用。
  • 如果内部事务提交异常,但被try/except块捕获,则foo()会被.on_commit回调,但bar()不会被回调

事务底层API(django.db.transaction)

自动提交(Autocommit)
  • get_autocommit(using = None)
  • set_autocommit(autocommit,using = None)
      a. 自动提交最初是打开的。如果通过set_autocommit(autocommit,using = None)关闭自动提交,则有责任恢复它。
      b. 关闭自动提交后,将获得数据库配置的的默认行为。
      c. 在重新打开自动提交之前,必须确保没有事务处于活动状态,通常通过发出commit()或rollback()。
      d. 当atomic()块处于活动状态时,Django将拒绝关闭自动提交,因为这会破坏原子性。
事务(Transactions)
  • commit(using=None)
  • rollback(using=None)
      a. 当执行一个事务时,可以选择提交(commit),也可以选择回滚(rollback)
      b. 当atomic()块处于活动状态时,Django将拒绝提交或回滚,因为这会破坏原子性。
保存点(Savepoints)
  • 保存点是事务中的标记,使您可以回滚部分事务,而不是完整事务。
  • 当使用atomic()装饰器嵌套时,它创建一个保存点来允许部分提交或回滚。
      .savepoint(using = None):创建一个新的保存点,返回保存点ID (sid)。
      .savepoint_commit(sid, using=None):提交保存点sid(提交部分事务)。
      savepoint_rollback(sid,using = None):将事务回滚到保存点sid。
      clean_savepoints(using = None):重置用于生成唯一保存点ID的计数器。
  • 保存点可以通过执行部分回滚从数据库错误中恢复。如果在atomic()块中执行此操作,那么整个块仍然会回滚,因为它不知道已经在较低的级别处理了这种情况。为了防止这种情况,可以使用以下函数控制回滚行为:
      get_rollback(using=None):获取”需要回滚“标志
      set_rollback(rollback, using=None):设置或取消设置”需要回滚“标志
      设置rollback=True在退出最内层原子块时强制回滚。有助于在不引发异常的情况下触发回滚。
      设置rollback=False可以防止这样的回滚,但要确保已将事务回滚到当前原子块中已知的良好保存点,否则就会破坏原子性,并可能发生数据损坏。

你可能感兴趣的:(Django之数据库事务)