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
相关产品推荐
相关产品推荐

