Django Rest Framework向request.data添加列表元素出现嵌套列表问题求解
问题原因
出现嵌套列表报错的根因是QueryDict类型的特殊存储逻辑:Django的QueryDict用来处理多值字段时,直接赋值或append操作会把传入的整个列表当作单个元素存入对应key的value列表中,最终生成[['3','4']]的嵌套结构,序列化器无法将嵌套列表解析为合法UUID格式。
解决方案
方案1:兼容QueryDict原生方法处理
QueryDict自带setlist方法专门用来给多值字段赋值一维列表,修改后代码如下:
def post(self, request, format=None): """ Creates a Post instance """ is_querydict = isinstance(request.data, QueryDict) if is_querydict: request.data._mutable = True tmp_new_tags = request.data.get('new_tags', None) tag_ids = [] if tmp_new_tags: new_tags = request.data.pop('new_tags') # 兼容QueryDict.pop返回列表的特性 tag_list = new_tags[0] if is_querydict and isinstance(new_tags[0], list) else new_tags for new_tag in tag_list: tag = Tag.objects.create(name=new_tag, user=self.request.user) tag_ids.append(str(tag.id)) # 分类型给tags赋值 if is_querydict: request.data.setlist('tags', tag_ids) else: request.data['tags'] = tag_ids serializer = PostSerializer(data=request.data) if serializer.is_valid(): serializer.save(user=self.request.user) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
方案2:拷贝数据独立处理(更推荐)
不要直接修改原始request.data,拷贝出独立数据处理完成后再传给序列化器,完全规避QueryDict的特性问题:
def post(self, request, format=None): """ Creates a Post instance """ # 拷贝请求数据避免修改原始request req_data = request.data.copy() tmp_new_tags = req_data.pop('new_tags', None) tag_ids = [] if tmp_new_tags: # 兼容QueryDict返回列表的特殊情况 if isinstance(tmp_new_tags, list) and len(tmp_new_tags) == 1 and isinstance(tmp_new_tags[0], list): tmp_new_tags = tmp_new_tags[0] for new_tag in tmp_new_tags: tag = Tag.objects.create(name=new_tag, user=self.request.user) tag_ids.append(str(tag.id)) req_data['tags'] = tag_ids serializer = PostSerializer(data=req_data) if serializer.is_valid(): serializer.save(user=self.request.user) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
补充校验
需要确认PostSerializer中tags字段显式设置many=True,否则序列化器默认按单值处理,也会引发解析报错:
class PostSerializer(serializers.ModelSerializer): """ Serializer for Post create, update, delete model """ tags = serializers.PrimaryKeyRelatedField(queryset=Tag.objects.all(), many=True) class Meta: model = Post fields = ('title', 'body', 'description', 'publish', 'tags')
如果前端请求直接传Content-Type: application/json格式参数,request.data会默认为普通Python字典,不会触发QueryDict相关问题,也不需要修改_mutable属性。
内容的提问来源于stack exchange,提问作者Fer
相关产品推荐
相关产品推荐

