Django Rest Framework中validated_data丢失数据问题排查
核心原因
你的request.data中,ingredients和directions字段是JSON字符串数组(例如'{"name":"dfgdfg","amount":"gdfgd"}'),但RecipeSerializer中的嵌套序列化器(IngredientSerializer/DirectionSerializer)期望接收的是已经解析完成的Python字典对象,而非字符串。序列化器无法自动将JSON字符串转换为所需的字典结构,导致验证失败——这就是为什么你必须添加required=False才能通过基础验证,但validated_data中仍然没有这两个字段的原因。
另外,name和description字段的值带有额外引号(如'"fdgdfgdf"'),也是前端发送格式的问题,导致你需要在create方法中手动去除引号。
解决方案
方案1:前端调整数据发送格式(推荐)
如果可以修改前端代码,不要将JSON字符串塞入FormData,而是使用DRF支持的嵌套字段表单格式传递数组:
- 对于
ingredients数组,循环每个元素,用ingredients[索引][字段名]作为FormData的键,示例代码:const formData = new FormData(); // 处理ingredients ingredients.forEach((ingredient, index) => { formData.append(`ingredients[${index}][name]`, ingredient.name); formData.append(`ingredients[${index}][amount]`, ingredient.amount); }); // 处理directions directions.forEach((direction, index) => { formData.append(`directions[${index}][content]`, direction.content); }); // 添加其他字段 formData.append('name', name); formData.append('description', description); formData.append('image', imageFile);
这种格式下,DRF会自动将参数解析为嵌套的字典数组,序列化器可以直接识别并验证,无需后端额外处理。
方案2:后端手动解析JSON字符串
如果前端无法修改,在视图中先解析request.data中的JSON字符串,再传入序列化器:
修改views.py的POST逻辑:
@api_view(["GET","POST"]) def api_list(request): if request.method=="GET": data = Recipe.objects.all() serializer = RecipeSerializer(data, many=True) return Response(serializer.data) elif request.method=="POST": import json # 复制request.data(QueryDict默认不可变) data = request.data.copy() # 解析ingredients字段 if 'ingredients' in data: try: # 逐个解析JSON字符串为字典 data['ingredients'] = [json.loads(item) for item in data.getlist('ingredients')] except json.JSONDecodeError: return Response({"error": "无效的ingredients JSON格式"}, status=status.HTTP_400_BAD_REQUEST) # 解析directions字段 if 'directions' in data: try: data['directions'] = [json.loads(item) for item in data.getlist('directions')] except json.JSONDecodeError: return Response({"error": "无效的directions JSON格式"}, status=status.HTTP_400_BAD_REQUEST) serializer = RecipeSerializer(data=data) print("Validating..") if serializer.is_valid(): print("validated!") serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) print (serializer.errors) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
处理完成后,serializer接收的data中ingredients和directions会是字典数组,嵌套序列化器可以正常验证并将其纳入validated_data,此时你可以移除序列化器字段中的required=False。
额外优化
针对name和description字段的引号问题,建议前端直接传递纯字符串值(不要带引号),这样后端无需在create方法中手动执行replace('"','')操作,避免额外的字符串处理逻辑。
内容的提问来源于stack exchange,提问作者Thanh Nguyen

