Django基础:transaction事务

发布于 2020-12-03  872 次阅读


有些时候我们需要对数据库进行一连串的操作,如果其中某一个操作失败,那么其他的操作也要跟着回滚到操作以前的状态。

例如用户A给用户B转账,A账户减少100元,B账户增加100元,如果A账户减少了100元,B账户增加100元时却失败了,A平白无故损失了100元,这种情况是不可接受的。要么全部成功要么全部失败,不能出现部分成功、部分失败!

Django中 @transaction.atomic()装饰器可实现事务操作如下:

@transaction.atomic()
def addProject(request, **kwargs):
    """
    新增项目
    :param request:
    :return:
    """

    project_name = request.POST.get('projectName', None)
    project_type = request.POST.get('projectType', None)

    if project_name is None or len(project_name) < 1 or len(project_name) > 32:
        return JsonResponse({"statusCode": "130001",
                             "message": "项目名称为空或者太长"},
                            status=status.HTTP_200_OK)

    if project_type is None or int(project_type) < 0 or int(project_type) > 5:
        return JsonResponse(
            {"statusCode": "130002", "message": "无效项目类型"}, status=status.HTTP_200_OK)

    uf = ProjectForm(request.POST)  # 绑定POST动作

    sid = transaction.savepoint()

    if uf.is_valid():
        # 保存项目
        project = uf.save()
        project = Project.objects.get(id=project.id)
        userid = request.COOKIES.get("id", None)
        user = User.objects.get(id=userid)

        try:
            # 保存用户项目关系
            conn_project = Users(project=project, user=user, userType=0)
            conn_project.save()

            # 添加默认接口分组
            groupName = "默认分组"
            groupname = Group(
                project=project,
                groupName=groupName,
                parentGroupID=0,
                isChild=0)
            groupname.save()

            # 添加状态码默认分组
            statuscodegroup = Code_Group(
                project=project,
                groupName=groupName,
                parentGroupID=0,
                isChild=0)
            statuscodegroup.save()

            # 添加项目文档默认分组
            documentgroup = Document_Group(
                project=project,
                groupName=groupName,
                parentGroupID=0,
                isChild=0)
            documentgroup.save()

            # 添加定时任务默认分组
            crontab_group = Crontab_Group(project=project,
                                          groupName=groupName,
                                          parentGroupID=0,
                                          isChild=0)
            crontab_group.save()

            # 添加操作记录
            save_operation_log(
                0,
                user,
                '创建项目:' +
                project_name,
                project,
                0,
                project.id)
            transaction.savepoint_commit(sid)

            return JsonResponse(
                {
                    "projectInfo": {
                        "projectType": project.projectType,
                        "projectID": project.id,
                        "projectUpdateTime": project.projectUpdateTime,
                        "projectVersion": project.projectVersion},
                    "statusCode": "000000"},
                status=status.HTTP_200_OK)
        except BaseException as e:
            transaction.savepoint_rollback(sid)
            logger.error('新增项目失败:' + str(e))
    return JsonResponse({"status": "130000",
                         "messages": "新增项目失败"},
                        status=status.HTTP_400_BAD_REQUEST)

以上代码实现新增项目操作,新增项目后创建用户关系、状态码、文档等相关操作,其中任何一个步骤发送异常所有相关表都会恢复到项目创建前状态。

还有最后一个大杀器。如果你想让所有的数据库操作都是事务,那就在 settings.py 里配置:

# settings.py

# 以 sqlite 为例
DATABASES = {
    'default': {
        'ENGINE': ...,
        'NAME': ...,
        # 加上这条
        'ATOMIC_REQUESTS': True,
    }
}

然后可以用 non_atomic_requests 标记不需要成为事务的视图:

@transaction.non_atomic_requests
def create_project(request):
    ...

另外,类视图也是可以成为事务的:

class CreateProject(View):
    @transaction.atomic
    def get(self, request):
        ...

最后总结一下,并非任意对数据库的操作序列都是事务。数据库事务拥有 ACID特性

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

官方文档:https://docs.djangoproject.com/en/3.0/topics/db/transactions/


一名测试工作者,专注接口测试、自动化测试、性能测试、Python技术。