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

Django Rest Framework:MSSQL存储过程的数据建模与序列化

我来帮你一步步解决这个问题,咱们从当前代码的问题点入手,逐步调整到符合你的需求:

1. 修正数据库连接与存储过程调用(规避全局Cursor风险)

全局声明的priority Cursor在多请求并发场景下会引发冲突,而且直接用f-string拼接SQL存在SQL注入风险。我们改用参数化查询+上下文管理器来安全处理数据库连接:

# models.py
from django.db import models, connections

class GetWorkOrder(models.Model):
    # 只定义正常结果集的字段,错误结果单独处理
    PartNumber = models.CharField(db_column='PartNumber', max_length=50)
    SerialNumber = models.CharField(db_column='SerialNumber', max_length=50)
    Index = models.IntegerField(db_column='Index')

    class Meta:
        managed = False  # 标记为非Django管理的表(仅用于序列化存储过程结果)
        db_table = 'dummy_workorder'  # 随便填一个占位表名即可

    @staticmethod
    def serials(param1, param2):
        results = []
        error_msg = None
        # 用上下文管理器自动管理Cursor的打开/关闭
        with connections['priority'].cursor() as cursor:
            try:
                # 参数化查询避免SQL注入,不要用f-string拼接!
                cursor.execute('EXEC [system].[dbo].[Stored_Procedure] %s, %s', [param1, param2])
                # 获取第一个结果集并判断列数
                first_result = cursor.fetchall()
                if cursor.description:
                    column_count = len(cursor.description)
                    if column_count == 3:
                        # 正常结果:映射到GetWorkOrder模型
                        results = [GetWorkOrder(*row) for row in first_result]
                    elif column_count == 1:
                        # 错误结果:提取错误信息
                        if first_result:
                            error_msg = first_result[0][0]
                # 遍历剩余结果集(存储过程可能返回多个,按需处理)
                while cursor.nextset():
                    pass
            except Exception as e:
                error_msg = f"数据库调用错误:{str(e)}"
        return results, error_msg

2. 重构请求参数与响应序列化逻辑

你之前的Serializer用错了场景,to_internal_value不是用来验证GET请求参数的。我们拆分出三个Serializer,分别处理请求参数验证、子项序列化、最终响应结构组装:

# serializers.py
from rest_framework import serializers
from .models import GetWorkOrder

# 验证GET请求的query参数
class RequestParamSerializer(serializers.Serializer):
    param1 = serializers.CharField(required=True, max_length=50, error_messages={"required": "param1为必填项"})
    param2 = serializers.CharField(required=True, max_length=50, error_messages={"required": "param2为必填项"})

# 序列化SerialNumber子项
class SerialNumberItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = GetWorkOrder
        fields = ('SerialNumber', 'Index')

# 组装你需要的最终响应结构
class WorkOrderResponseSerializer(serializers.Serializer):
    WorkOrder = serializers.CharField()
    PartNumber = serializers.CharField()
    SerialNumberItems = SerialNumberItemSerializer(many=True)

3. 调整View逻辑:串联验证、调用、响应流程

现在修改View,整合所有逻辑,处理正常/错误两种场景:

# views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import GetWorkOrder
from .serializers import RequestParamSerializer, WorkOrderResponseSerializer

@api_view(['GET'])
def top_serial2(request, format=None):
    # 第一步:验证请求参数
    param_serializer = RequestParamSerializer(data=request.query_params)
    if not param_serializer.is_valid():
        return Response(param_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    validated_data = param_serializer.validated_data
    param1 = validated_data['param1']
    param2 = validated_data['param2']

    # 第二步:调用存储过程获取结果
    results, error_msg = GetWorkOrder.serials(param1, param2)

    # 第三步:处理错误场景
    if error_msg:
        return Response(
            {'ErrMessage': error_msg},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )
    
    # 第四步:处理空结果场景
    if not results:
        return Response(
            {'ErrMessage': '未查询到相关数据'},
            status=status.HTTP_404_NOT_FOUND
        )
    
    # 第五步:组装期望的响应结构
    # 假设WorkOrder取自param2,PartNumber取第一个结果的对应字段(可根据实际调整)
    response_data = {
        'WorkOrder': param2,
        'PartNumber': results[0].PartNumber,
        'SerialNumberItems': results
    }

    # 序列化响应并返回
    response_serializer = WorkOrderResponseSerializer(data=response_data)
    response_serializer.is_valid(raise_exception=True)
    return Response(response_serializer.data)

关键细节说明

  • SQL注入防护:必须用参数化查询,绝对不能直接拼接用户输入到SQL语句中,这是后端安全的基本要求。
  • 多结果集处理:MSSQL存储过程可能返回多个结果集,用cursor.nextset()遍历确保不会遗漏。
  • 响应结构匹配:完全按照你要求的JSON结构组装数据,错误场景返回清晰的错误信息。
  • Model的managed=False:因为这个Model仅用于序列化存储过程结果,不是数据库中实际存在的表,所以标记为非Django管理,避免Django尝试创建表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 18:12:51