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

Django Rest Framework嵌套序列化器保存Live_In实例报错求助

解决Django Rest Framework嵌套序列化器更新时的AttributeError问题

你的问题主要出在两个核心点上:一是嵌套字段的source配置导致反序列化的数据结构不符合预期,二是序列化返回时source未正确生效,DRF错误地尝试从User实例直接获取不存在的live_in属性。

错误原因拆解

  1. 反序列化数据结构错位:你给live_in字段设置了source='profile.live_in',DRF会自动把前端发送的live_in数据放到validated_data的profile子字典里,而不是根层级。但你的update方法却在根级validated_data中查找live_in,导致这部分更新逻辑完全不会触发,用户提交的live_in数据被直接忽略。
  2. 序列化返回时的属性查找错误:由于DRF对嵌套序列化器source的处理逻辑问题,返回响应时它没有按照你指定的profile.live_in路径获取数据,而是直接尝试读取User实例的live_in属性,自然抛出AttributeError。

修改后的完整序列化器代码

我们可以去掉source配置,改用to_representation方法手动处理序列化逻辑,同时调整update方法来正确解析数据:

class CityLiveInSerializer(ModelSerializer):
    country = CharField(required=False, source='country.name')
    
    class Meta:
        model = City
        fields = ['name', 'slug', 'country']

class UserEditSerializer(ModelSerializer):
    live_in = CityLiveInSerializer(required=False)
    about = serializers.CharField(source='profile.about', required=False)
    
    class Meta:
        model = User
        fields = ['username', 'live_in', 'about']
    
    def to_representation(self, instance):
        """手动处理序列化逻辑,从profile中读取live_in数据"""
        data = super().to_representation(instance)
        # 根据profile的live_in状态返回对应序列化结果
        if instance.profile.live_in:
            data['live_in'] = CityLiveInSerializer(instance.profile.live_in).data
        else:
            data['live_in'] = None
        return data
    
    def update(self, instance, validated_data):
        # 更新用户名
        instance.username = validated_data.get('username', instance.username)
        instance.save()
        
        # 分离profile数据和live_in数据
        profile_data = validated_data.pop('profile', {})
        live_in_data = validated_data.pop('live_in', None)
        
        # 更新profile的about字段
        profile = instance.profile
        if profile_data.get('about') is not None:
            profile.about = profile_data.get('about', profile.about)
        
        # 更新profile的live_in字段
        if live_in_data:
            # 查找或创建目标城市
            try:
                city = City.objects.get(name=live_in_data['name'])
            except City.DoesNotExist:
                # 处理国家字段:将前端传入的国家名称转换为Country实例
                country_name = live_in_data.pop('country', None)
                country = None
                if country_name:
                    try:
                        country = Country.objects.get(name=country_name)
                    except Country.DoesNotExist:
                        country = Country.objects.create(name=country_name)
                live_in_data['country'] = country
                city = City.objects.create(**live_in_data)
            profile.live_in = city
        
        # 统一保存profile的所有更新,减少数据库操作
        profile.save()
        
        return instance

关键修改说明

  1. 移除live_in字段的source:让DRF将前端发送的live_in数据直接放到根级validated_data中,方便我们在update方法里直接获取。
  2. 新增to_representation方法:手动从User的profile中读取live_in数据并序列化,彻底避免DRF错误地查找User实例的live_in属性。
  3. 优化update逻辑:
    • 直接从根级validated_data取出live_in_data进行处理
    • 新增国家字段的转换逻辑:因为CityLiveInSerializer返回的是国家名称字符串,而City模型的country是ForeignKey,必须转换为实例才能保存
    • 统一保存profile的所有更新,减少不必要的数据库交互

额外注意事项

  • 确保所有User实例都有对应的Profile记录:可以通过Django信号在创建User时自动生成Profile,避免instance.profile抛出Profile.DoesNotExist异常。
  • 如果你的City模型的slug字段是自动生成的(比如继承自SlugModel),创建City时不需要手动传入slug值,框架会自动处理。

内容的提问来源于stack exchange,提问作者KitKit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:43:49