• django系列之事务操作


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

    1. 全局开启事务

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

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'testdb', 
            'USER': 'root',  
            'PASSWORD': '123', 
            'HOST': '127.0.0.1',     
            'PORT': 3306,  
            'ATOMIC_REQUESTS': True  # 全局开启事务,和http请求的整个过程绑定在一起
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

    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 装饰,不受全局事务配置的控制。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    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 仅作用于视图本身时才会生效。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2 局部开启事务

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

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

    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"})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

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

    with transaction.atomic():
        AuthorDetail.objects.create(**valid_data_test)
    
    • 1
    • 2

    3. 显式开启保存点

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

    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"})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4. 事务嵌套

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

    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都没有入库。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    基本Dos命令
    6.3.2 基于DMG文件安装MySQL
    webpack之hot热更新
    Linux 内核参数:meminfo
    CDN的基本概念
    一文看懂一体成型贴片电感感值怎么测量gujing
    手把手教你玩转 Gitea|在 Windows 系统上安装 Gitea
    Python SQLite3 教程
    Python用yield from 实现异步协程爬虫
    Python文件读写模式
  • 原文地址:https://blog.csdn.net/CSDN1csdn1/article/details/133840978