DRF中带source的CharField未出现在validated_data中的问题
解决DRF序列化器中关联字段无法进入validated_data的问题
这个问题我之前也碰到过,本质是DRF对关联模型字段的默认处理逻辑在搞鬼。先还原你的场景:
你定义的Django模型:
class AppUser(models.Model): user = models.OneToOneField(User, related_name='app_user')
对应的DRF序列化器:
class AppUserSerializer(serializers.ModelSerializer): first_name = serializers.CharField(source='user.first_name') class Meta: model = AppUser fields = ('first_name',) def create(self, validated_data): first_name = validated_data.pop('first_name') user = User.objects.create_user(username='test', first_name=first_name, last_name='test') app_user = AppUser.objects.create(**validated_data, user=user) return app_user
运行时遇到的错误:
KeyError: 'first_name'
调试后发现validated_data是空的,加write_only=True虽然能解决但不符合需求——这是因为DRF默认会把指向关联模型的字段(通过source关联的)设为只读,所以写入时不会把这个字段的内容放进validated_data里。
两种解决办法供你选:
办法一:显式设置read_only=False,调整取值逻辑
直接在字段定义里加上read_only=False,告诉DRF这个字段不是只读的,但要注意source带来的小变化:DRF会把first_name的数据包装到validated_data的user字典中,所以取值方式要改一下:
class AppUserSerializer(serializers.ModelSerializer): # 显式声明该字段可写 first_name = serializers.CharField(source='user.first_name', read_only=False) class Meta: model = AppUser fields = ('first_name',) def create(self, validated_data): # 从user字典中取出first_name user_data = validated_data.pop('user') first_name = user_data.get('first_name') user = User.objects.create_user(username='test', first_name=first_name, last_name='test') app_user = AppUser.objects.create(**validated_data, user=user) return app_user
办法二:不使用source,用to_representation处理读取逻辑
如果你想保留原来的validated_data.pop('first_name')逻辑,不想改取值方式,可以去掉source,转而在序列化器的to_representation方法里处理返回时的字段映射:
class AppUserSerializer(serializers.ModelSerializer): # 不需要source,直接定义字段并声明可写 first_name = serializers.CharField(read_only=False) class Meta: model = AppUser fields = ('first_name',) def create(self, validated_data): # 这里和你原来的代码完全一致,不需要修改 first_name = validated_data.pop('first_name') user = User.objects.create_user(username='test', first_name=first_name, last_name='test') app_user = AppUser.objects.create(**validated_data, user=user) return app_user def to_representation(self, instance): # 读取序列化数据时,把关联user的first_name赋值到返回结果里 representation = super().to_representation(instance) representation['first_name'] = instance.user.first_name return representation
这种写法下,创建时first_name会直接出现在validated_data中,读取时也能正确返回该字段的值,完美符合你的需求,不需要设置write_only。
内容的提问来源于stack exchange,提问作者Navid777
相关产品推荐
相关产品推荐

