Django 事务操作

如何在Django中进行事务操作

案例:

客户A要给客户B转一笔钱,这个在数据库中需要进行两步:

  1.客户A减钱

  2.客户B加钱

如果在第一步结束后,服务器出现异常,停下了,第二步没有进行,如果数据库使用了事务操作,真的出现异常的时候,前面的操作会进行回滚。

简单的说就是:要么全部执行成功,要么一个都不执行。

这个回滚的操作就叫做数据库的原子性操作。

但是,这是在MySQL数据库中,而我们在Django的ORM中如何进行呢?

显式控制事务

Django提供了一个API来控制数据库事务。

atomic(using = Nonesavepoint = True

原子性是数据库事务的定义属性。atomic 允许我们创建一个代码块,在该代码块中保证数据库的原子性。如果代码块成功完成,则更改将提交到数据库。如果存在异常,则回滚更改。


首先,我们要导入一个Django的内置模块

from django.db import transaction

接着,就可以使用了

from django.db import transaction

with transaction.atomic():
    # ORM操作
    pass

我们举个例子测试一下

方式一

在"try/except"块中开启事务(且用到with上下文管理器)

1.创建一个项目,新建一个APP(基础操作,这里不再赘述);

2.通过ORM创建生成表:

from django.db import models

class UserInfo(models.Model):
    username = models.CharField("用户",max_length=32)
    balance = models.CharField("余额",max_length=32)

注意啊:踩过的坑,涉及金融计算,涉及小数啊,要求特别精确的,我们用字符串存储。

如果是金融计算的话,我们用一个Decimal来进行计算。

3.我们给数据库加两条数据,用来模拟两个用户之间的转账;

4.配置URL;

5.创建对应的视图函数:

from django.shortcuts import render,HttpResponse
from app01 import models
from django.db import transaction
from django.db.models import F

def index(request):
    try:
        with transaction.atomic():
            models.UserInfo.object.filter(id=1).update(balance=F("balance")-100)
            models.UserInfo.object.filter(id=2).update(balance=F("balance")+100)
    except Exception as e:
        return HttpResponse("出现错误<%s>"%str(e))
    return HttpResponse("执行成功")

当我们访问index的时候,会进行一次转账操作。

6.现在,我们让他报错:

from django.shortcuts import render,HttpResponse
from app01 import models
from django.db import transaction
from django.db.models import F

def index(request):
    try:
        with transaction.atomic():
            models.UserInfo.object.filter(id=1).update(balance=F("balance")-100)
            raise '一个错误'
            models.UserInfo.object.filter(id=2).update(balance=F("balance")+100)
    except Exception as e:
        return HttpResponse("出现错误<%s>"%str(e))
    return HttpResponse("执行成功")

我们再次查看数据库文件,如果没有数据的原子性操作,我们第一条sql执行完报错,那钱肯定是减去了。

但是,我们进行的是原子性的操作,你会发现钱没有减诶。

完美,没毛病

这是常规的一种操作,另外还有一起其他的方式。

方式二

对整个view视图开启事务

from django.db import transaction

@transaction.atomic
def index(request):
    # ORM操作
    return ....

注意不要在原子块中进行错误捕获!

当退出原子块的时候,Django会去查看它是否正常退出或者是否有异常来确定是否提交或者回滚。

如果你捕获并处理了原子块中的异常,可能会隐藏Django中发生问题的事实。这样可能会造成非预期的行为。

以上,程序中并没有显示调用commit()提交方法和rollback()回滚方法,原因是Django默认设置为自动提交。当然你可以改变这一切,Django为此提供了相关API:get_autocommit(using=None)set_autocommit(autocommit, using=None)

 

为什么Django使用自动提交

在SQL标准中,每个SQL查询都会启动一个事务,除非一个事务已经处于活动状态。然后必须显式提交或回滚此类事务。

这对应用程序开发人员来说并不总是方便 为了缓解此问题,大多数数据库都提供自动提交模式。启用自动提交并且没有事务处于活动状态时,每个SQL查询都会包含在自己的事务中。换句话说,不仅每个这样的查询都会启动事务,而且事务也会自动提交或回滚,具体取决于查询是否成功。

 

事务中的保存点

保存点是事务中的标记,使您可以回滚部分事务,而不是完整事务。SQLite(≥3.6.8),PostgreSQL,Oracle和MySQL(使用InnoDB存储引擎时)后端可以使用保存点。其他后端提供了保存点功能,但它们是空操作 - 它们实际上并没有做任何事情。

如果您使用自动提交(Django的默认行为),则保存点不是特别有用。但是,一旦打开事务atomic(),就会构建一系列等待提交或回滚的数据库操作。如果发出回滚,则回滚整个事务。保存点提供了执行细粒度回滚的功能,而不是将执行的完全回滚transaction.rollback()

当atomic()装饰器嵌套时,它会创建一个保存点以允许部分提交或回滚。强烈建议您使用atomic()而不是下面描述的功能,但它们仍然是公共API的一部分,并且没有计划弃用它们。

这些函数中的每一个都接受一个using参数,参数应该是行为适用的数据库的名称。如果未using提供参数,则使用"default"数据库。

保存点由以下三个函数控制django.db.transaction:

1)savepoint(using = None

创建一个新的保存点。这标志着已知处于“良好”状态的交易中的一个点。返回保存点ID(sid)。

2)savepoint_commitsidusing = None

提交保存点sid自创建保存点以来执行的更改将成为事务的一部分。

3)savepoint_rollback(sidusing = None

将事务回滚到保存点sid

如果不支持保存点或数据库处于自动提交模式,则这些功能不执行任何操作。

另外,还有一个实用功能:

4)clean_savepoints(using = None

重置用于生成唯一保存点ID的计数器。

以下示例演示了保存点的用法:

from django.db import transaction

# open a transaction
@transaction.atomic
def viewfunc(request):

    a.save()
    # transaction now contains a.save()

    sid = transaction.savepoint()

    b.save()
    # 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()

保存点可用于通过执行部分回滚从数据库错误中恢复。如果你在一个atomic()区块内执行此操作,整个区块仍将被回滚,因为它不知道你已经处理了较低级别的情况!为防止这种情况,您可以使用以下函数控制回滚行为。

get_rollbackusing = None
set_rollbackrollback,using = None

设置回滚标志以True在退出最内层原子块时强制回滚。这可能有助于在不引发异常的情况下触发回滚。

将其设置为False阻止此类回滚。在此之前,请确保您已将事务回滚到当前原子块中的已知良好保存点!否则,您将破坏原子性并可能发生数据损坏。

 

  至此。转载请注明出处。

本站相关链接:>>Django部署 ]

 

posted @ 2018-10-15 16:23  失恋的蔷薇  阅读(625)  评论(0编辑  收藏  举报