使用Django REST Framework进行部分更新时出现KeyError问题
解决Django REST Framework Partial Update(Patch)中的KeyError 'profile'问题
让我来帮你分析下问题所在:你的UserSerializer的update方法里直接用validated_data.pop('profile')来获取嵌套的profile数据,但当你发送不包含profile字段的Patch请求(比如你只更新username)时,validated_data里根本没有'profile'这个键,自然就触发了KeyError。另外你的update方法里还有个潜在问题:不管请求里有没有传password,你都强制调用user.set_password(validated_data['password']),这同样会在Patch没传密码时报错。
下面是修复后的完整方案:
1. 修正UserSerializer的update方法
我们需要做三个关键调整:
- 用安全方式获取profile数据,避免KeyError
- 只在请求包含password字段时才更新密码
- 用嵌套序列化器处理profile的部分更新,让代码更可扩展
修改后的序列化器代码:
class UserProfileSerializer(serializers.ModelSerializer): class Meta: model = Profile exclude = ("user", ) class UserSerializer(serializers.ModelSerializer): profile = UserProfileSerializer(partial=True) def create(self, validated_data): profile_data = validated_data.pop('profile') user = super(UserSerializer, self).create(validated_data) # 创建时必须设置密码,直接处理 user.set_password(validated_data['password']) user.save() # 用序列化器更新profile更规范 profile_serializer = UserProfileSerializer(user.profile, data=profile_data) if profile_serializer.is_valid(): profile_serializer.save() return user def update(self, instance, validated_data): # 安全获取profile数据,没有则返回空字典 profile_data = validated_data.pop('profile', {}) # 先更新user的基础字段 user = super().update(instance, validated_data) # 仅当请求包含password时才更新密码 if 'password' in validated_data: user.set_password(validated_data['password']) user.save() # 有profile数据时才更新 if profile_data: profile_serializer = UserProfileSerializer(user.profile, data=profile_data, partial=True) if profile_serializer.is_valid(): profile_serializer.save() return user class Meta: model = User exclude = ("groups", "user_permissions", )
2. 移除自定义的partial_update方法
你不需要在UserViewSet里写自己的partial_update方法,因为ModelViewSet已经集成了UpdateModelMixin,它会自动处理Partial Update请求,只要你的序列化器支持partial模式(你已经在profile = UserProfileSerializer(partial=True)里设置了)。
你的视图可以保持原来的样子:
class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer model = User
测试验证
现在你发送Patch请求{ "username": "insomnia77" }时:
validated_data里没有'profile'键,profile_data会是空字典- 代码会跳过profile的更新逻辑,只更新username字段
- 不会再触发KeyError
如果需要同时更新profile字段,比如发送:
{ "username": "insomnia77", "profile": { "company": "New Company" } }
序列化器也会正确处理profile的部分更新。
内容的提问来源于stack exchange,提问作者Joey Fran
相关产品推荐
相关产品推荐

