使用DRF和Ajax保存用户资料时出现KeyError: 'profile'求助
解决DRF嵌套序列化器的KeyError问题
你遇到的KeyError: 'profile'核心原因很明确:前端发送的数据结构和后端嵌套序列化器期望的格式不匹配。你的UserSerializer将profile定义为嵌套的ProfileSerializer,所以DRF要求客户端发送的数据必须把Profile相关字段包裹在profile这个键下,但你的Ajax代码是把所有字段平级发送的,导致后端无法从validated_data中取出profile键。
下面分步骤给出修复方案:
1. 修正前端Ajax的数据结构
首先要调整profileData的结构,把属于Profile模型的字段(avatar、dob、headline等)放到profile嵌套对象中,User模型的字段(first_name、last_name)保持平级:
$("#saveProfile").click(function(e) { e.preventDefault(); var profileData = { // User模型字段 'first_name': $("#first_name").val(), 'last_name': $("#last_name").val(), // Profile模型字段,嵌套在profile键下 'profile': { 'avatar': $("#avatar").val(), 'country': $("#country").val(), 'location': $("#location").val(), 'cp': $("#postal_code").val(), 'headline': $("#headline").val(), 'dob': $("#dob").val(), 'facebook': $("#facebook").val(), 'twitter': $("#twitter").val(), 'github': $("#github").val(), 'website': $("#website").val() // 如果background有上传控件,也需要添加进来 // 'background': $("#background").val(), } } $.ajax({ type: 'PATCH', url: "{% url 'profiles:api_profile' username=request.user.username %}", data: JSON.stringify(profileData), contentType: 'application/json; charset=utf-8', success: function(res) { console.log(res.data); }, error: function(res) { console.log(res); } }); });
2. 修复视图层的返回逻辑
你的patch方法存在一个小问题:调用super().patch()后,你重新获取了实例并序列化,但其实可以直接基于更新后的实例返回数据,确保返回的是最新的用户资料:
class ProfileRetrieveAPIView(generics.RetrieveUpdateDestroyAPIView): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (IsAuthenticatedOrReadOnly, ) lookup_field = 'username' def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) response = { "message": "Successfully retrieved", "data": serializer.data, "status_code": status.HTTP_200_OK } return Response(response) def patch(self, request, *args, **kwargs): instance = self.get_object() # 执行更新操作 serializer = self.get_serializer(instance, data=request.data, partial=True) serializer.is_valid(raise_exception=True) updated_instance = serializer.save() # 序列化更新后的实例 updated_serializer = self.get_serializer(updated_instance) response = { "message": "Perfil actualizado con éxito", "data": updated_serializer.data, "status_code": status.HTTP_200_OK } return Response(response, status=status.HTTP_200_OK)
3. 处理文件上传的额外说明
如果你的avatar或background是图片上传字段,上面的JSON格式无法传输文件,需要改用FormData来处理:
$("#saveProfile").click(function(e) { e.preventDefault(); var formData = new FormData(); // User字段 formData.append('first_name', $("#first_name").val()); formData.append('last_name', $("#last_name").val()); // Profile字段,使用嵌套格式 formData.append('profile[avatar]', $("#avatar")[0].files[0]); formData.append('profile[country]', $("#country").val()); formData.append('profile[location]', $("#location").val()); // 其他Profile字段... $.ajax({ type: 'PATCH', url: "{% url 'profiles:api_profile' username=request.user.username %}", data: formData, contentType: false, // 让浏览器自动设置正确的Content-Type processData: false, // 禁止jQuery处理FormData success: function(res) { console.log(res.data); }, error: function(res) { console.log(res); } }); });
这样调整后,后端的validated_data中就会包含profile键,你的update方法就能正常取出Profile数据进行更新了。
内容的提问来源于stack exchange,提问作者Miguel Toledano
相关产品推荐
相关产品推荐

