You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django多模型原子事务未回滚:视图代码错误排查求助

Django事务不回滚的问题分析与修复

问题根源

你代码里的transaction.atomic()上下文内部嵌套了一个try-except块,当创建Eliel_FuncionarioAlocacaoCentro出现异常时,这个内部的except直接捕获了异常并返回了响应,没有把异常抛出到外层的事务上下文。Django的原子事务机制要求:只有当transaction.atomic()的上下文范围内出现未被捕获的异常时,才会触发回滚操作;如果异常被内部捕获并处理,事务会默认提交,这就导致Eliel_FuncionarioAlocacao的记录被保留,而关联的中心记录创建失败的情况。

修复方案

有两种可行的修复方式:

  • 移除内部的try-except块,让异常直接向外传播到transaction.atomic()的上下文,触发回滚后在更外层统一处理异常。
  • 若需要保留内部错误日志,可在捕获异常后先记录日志,再重新抛出异常,让事务上下文感知到异常并执行回滚。

修改后的代码示例

这里采用第二种方式,既保留日志记录,又确保事务回滚:

from .serializers import FuncionarioAlocacaoSerializer
from auditlog.context import set_actor
from client.app_eliel.models import Eliel_FuncionarioAlocacao, Eliel_FuncionarioAlocacaoCentro
from django.db import transaction
from microservices.serializers import ActorSerializer
from oauth2_provider.contrib.rest_framework import OAuth2Authentication, TokenHasReadWriteScope
from project_datafit.utils import log_error
from rest_framework import status
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response
import json
import re

@api_view(['POST'])
@authentication_classes([OAuth2Authentication])
@permission_classes([TokenHasReadWriteScope])
def funcionarioAlocacao_create_api(request):

    actor_serializer = ActorSerializer(data=request.data)
    funcionarioAlocacao_serializer = FuncionarioAlocacaoSerializer(data=request.data)
    actor = None

    try:
        actor_serializer.is_valid(raise_exception=True)
        funcionarioAlocacao_serializer.is_valid(raise_exception=True)

        actor = actor_serializer.validated_data['actor']
        matricula = funcionarioAlocacao_serializer.validated_data['matricula']
        data_inicio = funcionarioAlocacao_serializer.validated_data['data_inicio']
        data_fim = funcionarioAlocacao_serializer.validated_data['data_fim']
        obs = funcionarioAlocacao_serializer.validated_data.get('obs') 
        centros_data = funcionarioAlocacao_serializer.validated_data.get('centros', [])

        with transaction.atomic(): 
            with set_actor(actor):
                try:
                    funcionarioAlocacao = Eliel_FuncionarioAlocacao(Matricula=matricula, DataInicio=data_inicio, DataFim=data_fim, Obs=obs)
                    funcionarioAlocacao.save()

                    for centro_data_str in centros_data:
                        centro_data = json.loads(centro_data_str)
                        centro = centro_data.get('centro')
                        porcentagem = centro_data.get('porcentagem')

                        Eliel_FuncionarioAlocacaoCentro.objects.create(
                            IdAlocacao=funcionarioAlocacao,
                            CodCentro=centro,
                            Peso=porcentagem
                        )
                except Exception as e:
                    log_error(request.path, str(e), actor)
                    raise  # 重新抛出异常,触发事务回滚
        
            return Response({'success': 'Alocação feita com sucesso'}, status=status.HTTP_200_OK)
    except ValidationError as e:
        error_message = str(e.detail)
        error_message = re.search(r"ErrorDetail\(string='([^']*)'", error_message).group(1)
        return Response({'error': error_message}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({'error': 'Erro ao alocar funcionário'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

关键原理说明

Django的transaction.atomic()上下文管理器会在进入时开启一个事务:

  • 当上下文正常退出(无异常)时,自动提交事务;
  • 当上下文内抛出未被捕获的异常时,回滚整个事务。
    你之前的代码把异常在内部捕获并返回响应,相当于上下文正常退出,所以事务被提交,导致部分数据被保存。

内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 16:30:33