You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:20:44