Django REST Framework处理multipart/form-data时报category字段必填错误
问题根因
你遇到的报错是三个原因共同导致的:
- DRF 自带的
MultiPartParser、FormParser不支持解析category[0]、variants[0][price]这种带方括号的嵌套表单格式,这类字段会被识别为独立的零散字段,不会被整合成category、variants数组传递给序列化器 - 你的序列化器中
category字段对应模型的多对多关系,没有显式指定many=True,即使拿到数组也会校验不通过 - 额外注意:请求体里的字段名是
service,但模型和序列化器里定义的是services,字段名不匹配也会导致校验失败
修复方案
第一步:修正序列化器配置
给 category 字段显式指定多对多属性:
class AddProductSerializer(serializers.ModelSerializer): id = serializers.PrimaryKeyRelatedField(read_only=True) variants = VariantSerializer(many=True) slug = serializers.SlugField(read_only=True) # 新增:指定category为多值主键字段 category = serializers.PrimaryKeyRelatedField(many=True, queryset=Category.objects.all()) class Meta: model = Product fields = ['id','merchant','featured', 'top_rated','category','brand','collection','sub_category', 'name','slug','description', 'main_product_image','best_seller','picture', 'rating','availability','warranty','services','variants'] # 其余create逻辑保持不变
第二步:处理嵌套表单解析问题
两种方案可选:
方案1:前端改造(成本更低)
前端构造FormData时,将category、variants这类数组字段转为JSON字符串传递,后端序列化器新增自定义反序列化逻辑:
# 序列化器新增字段处理逻辑 import json class AddProductSerializer(serializers.ModelSerializer): # ... 其余字段不变 def to_internal_value(self, data): # 解析JSON格式的数组字段 if isinstance(data.get('category'), str): data['category'] = json.loads(data['category']) if isinstance(data.get('variants'), str): data['variants'] = json.loads(data['variants']) return super().to_internal_value(data)
方案2:后端自定义解析器(适配原有前端传参格式)
自定义支持嵌套表单的解析器,替换原有视图的parser_classes:
from rest_framework.parsers import MultiPartParser import re from django.utils.datastructures import MultiValueDict class NestedMultiPartParser(MultiPartParser): def parse(self, stream, media_type=None, parser_context=None): parsed = super().parse(stream, media_type, parser_context) data = {} for key, value in parsed.data.items(): # 解析带方括号的嵌套键 keys = re.findall(r'([^\[\]]+)', key) current = data for i, k in enumerate(keys[:-1]): if k not in current: if keys[i+1].isdigit(): current[k] = [] else: current[k] = {} current = current[k] last_k = keys[-1] if isinstance(current, list): idx = int(last_k) while len(current) <= idx: current.append({}) current[idx] = value else: current[last_k] = value parsed.data = MultiValueDict(data) return parsed
然后修改视图的parser配置:
class ProductAddAPIView(CreateAPIView): permission_classes = [IsAuthenticated] # 替换为自定义的嵌套解析器 parser_classes = [NestedMultiPartParser, JSONParser] serializer_class = AddProductSerializer
第三步:修正请求字段名
将请求体中的service字段改为services,和模型定义保持一致。
内容的提问来源于stack exchange,提问作者Reactoo
相关产品推荐
相关产品推荐

