django系列之事务操作

Django 默认的事务行为是自动提交,除非事务正在执行,否则每个查询将会马上自动提交到数据库。

1. 全局开启事务

在 Web 里,处理事务比较常用的方式是将每个请求封装在一个事务中。 在你想启用该行为的数据库中,把 settings 配置数据库中的参数 ATOMIC_REQUESTS 设置为 True。

python 复制代码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'testdb', 
        'USER': 'root',  
        'PASSWORD': '123', 
        'HOST': '127.0.0.1',     
        'PORT': 3306,  
        'ATOMIC_REQUESTS': True  # 全局开启事务,和http请求的整个过程绑定在一起
    }
}

它是这样工作的:在调用视图方法前,Django 先生成一个事务。如果响应能正常生成,Django 会提交该事务。而如果视图出现异常,Django 则会回滚该事务。

如果你全局开启了事务,你仍然可以使用 non_atomic_requests 装饰器让某些视图方法不受事务控制,但需要注意的是,该装饰器仅作用于视图本身时才会生效,在DRF的viewset中不生效。

python 复制代码
from django.db import transaction

@transaction.non_atomic_requests
def trans2():
    valid_data_test = {'gender': "male", 'birth': '2020-10-01', 'tele': 18812341234, 'addr': '南京市雨花台区'}
    res_test = AuthorDetail.objects.create(**valid_data_test)
    res_test += 1
    return Response(status=HTTP_202_ACCEPTED, data={"msg": "insert success"})

视图请求trans2被 @transaction.non_atomic_requests 装饰,不受全局事务配置的控制。
python 复制代码
class AuthorViewSet(viewsets.ModelViewSet):
    serializer_class = AuthorDetailSerializer

    @transaction.non_atomic_requests
    @action(methods='post', detail=False)
    def trans(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            valid_data = serializer.validated_data
            AuthorDetail.objects.create(**valid_data)
        return Response(status=HTTP_202_ACCEPTED, data={"msg": "insert success"})
     
DRF的视图函数即使被 @transaction.non_atomic_requests 装饰,但是依旧被全局事务配置所控制,所以说 non_atomic_requests 仅作用于视图本身时才会生效。

2 局部开启事务

Django项目中局部开启事务,可以借助于transaction.atomic方法。使用它我们就可以创建一个具备原子性的代码块,一旦代码块正常运行完毕,所有的修改会被提交到数据库。反之,如果有异常,更改会被回滚。

两种方式对某个请求使用事务:

python 复制代码
class AuthorViewSet(viewsets.ModelViewSet):
    serializer_class = AuthorDetailSerializer

    @transaction.atomic
    @action(methods='post', detail=False)
    def trans(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            valid_data = serializer.validated_data
            AuthorDetail.objects.create(**valid_data)
        return Response(status=HTTP_202_ACCEPTED, data={"msg": "insert success"})

@transaction.atomic
def trans2():
    valid_data_test = {'gender': "male", 'birth': '2020-10-01', 'tele': 18812341234, 'addr': '南京市雨花台区'}
    res_test = AuthorDetail.objects.create(**valid_data_test)
    res_test += 1
    return Response(status=HTTP_202_ACCEPTED, data={"msg": "insert success"})

对请求中的某个代码块使用事务:

python 复制代码
with transaction.atomic():
    AuthorDetail.objects.create(**valid_data_test)

3. 显式开启保存点

在事务操作中,我们还会经常显式地设置保存点(savepoint)。一旦发生异常或错误,我们使用savepoint_rollback方法让程序回滚到指定的保存点。如果没有问题,就使用savepoint_commit方法提交事务。

python 复制代码
class AuthorDetailViewSet(viewsets.ModelViewSet):

    serializer_class = AuthorDetailSerializer
    queryset = AuthorDetail.objects.all()

    @action(methods=["post"], detail=False)          # detail=False or True: True: 系统会自动在生成的路由中添加pk值
    def trans(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            valid_data = serializer.validated_data
            AuthorDetail.objects.create(**valid_data)
            valid_data_test = {'gender': "male", 'birthday': '2020-10-01', 'telephone': 18812341234, 'addr': '南京市雨花台区'}
            valid_data_test2 = {'gender': "male", 'birthday': '2020-10-02', 'telephone': 18812341234, 'addr': '南京市雨花台区'}
            with transaction.atomic():
                AuthorDetail.objects.create(**valid_data_test)
                sid = transaction.savepoint()
                try:
                    res_test2 = AuthorDetail.objects.create(**valid_data_test2)
                    res_test2 += 1
                except Exception:
                    transaction.savepoint_rollback(sid)
                    print("error and rollback")

                transaction.savepoint_commit(sid)
                print("commit success")

        return Response(status=HTTP_202_ACCEPTED, data={"msg": "insert success"})

4. 事务嵌套

事务嵌套,只有内外两层的事务都执行成功,那么事务才能最终被成功提交。如果内层事务执行失败,那么外层的事务也会失败,事务最终会提交失败。

python 复制代码
class AuthorDetailViewSet(viewsets.ModelViewSet):

    serializer_class = AuthorDetailSerializer
    queryset = AuthorDetail.objects.all()

    @transaction.atomic		# 外层事务的执行成功与否,除了它本身外,还依赖于内层事务执行成功与否。
    @action(methods=["post"], detail=False)          # detail=False or True: True: 系统会自动在生成的路由中添加pk值
    def trans(self, request):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            valid_data = serializer.validated_data
            AuthorDetail.objects.create(**valid_data)	# 记录1
            valid_data_test = {'gender': "male", 'birthday': '2020-10-01', 'telephone': 18812341234, 'addr': '南京市雨花台区'}

            with transaction.atomic():		# 故意让内层的事务执行失败
                res_test = AuthorDetail.objects.create(**valid_data_test)	# 记录2
                res_test += 1	

        return Response(status=HTTP_202_ACCEPTED, data={"msg": "insert success"})

最终,我们查得数据库的记录1和记录2都没有入库。
相关推荐
noravinsc20 分钟前
redis是内存级缓存吗
后端·python·django
betazhou30 分钟前
基于Linux环境实现Oracle goldengate远程抽取MySQL同步数据到MySQL
linux·数据库·mysql·oracle·ogg
百锦再44 分钟前
大数据技术的主要方向及其应用详解
大数据·linux·网络·python·django·pygame
lyrhhhhhhhh1 小时前
Spring 框架 JDBC 模板技术详解
java·数据库·spring
noravinsc2 小时前
django中用 InforSuite RDS 替代memcache
后端·python·django
喝醉的小喵2 小时前
【mysql】并发 Insert 的死锁问题 第二弹
数据库·后端·mysql·死锁
付出不多3 小时前
Linux——mysql主从复制与读写分离
数据库·mysql
初次见面我叫泰隆3 小时前
MySQL——1、数据库基础
数据库·adb
Chasing__Dreams3 小时前
Redis--基础知识点--26--过期删除策略 与 淘汰策略
数据库·redis·缓存
源码云商3 小时前
【带文档】网上点餐系统 springboot + vue 全栈项目实战(源码+数据库+万字说明文档)
数据库·vue.js·spring boot