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

DRF用户注册API提交数据触发AttributeError问题咨询

DRF用户注册API AttributeError问题解析

问题场景

我使用DRF开发用户注册API,采用与Django默认User模型一对一关联的Profile模型。基于User模型创建序列化器,并将Profile的字段定义为序列化器字段。提交数据时,用户和Profile已成功保存到数据库,但出现以下错误:

AttributeError at /auth/registration Got AttributeError when attempting to get a value for field address on serializer UserRegistrationSerializer. The serializer field might be named incorrectly and not match any attribute or key on the User instance. Original exception text was: 'User' object has no attribute 'address'.

为address字段添加write_only=True参数后问题解决,但想了解错误产生的原因,以及视图为何会访问address字段,却忽略phone_number和date_of_birth字段且未报错。

相关代码

models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    date_of_birth = models.DateField(null=True)
    phone_number = models.CharField(null=True, max_length=30)
    address = models.CharField(max_length=250)

    def __str__(self):
        return f'Profile of {self.user.username}'

serializers.py

class UserRegistrationSerializer(serializers.ModelSerializer):
    phone_number = serializers.CharField(required=False, max_length=30)
    date_of_birth = serializers.DateField(required=False)
    address = serializers.CharField(max_length=250)

    password = serializers.CharField(write_only=True)
    password2 = serializers.CharField(write_only=True, label = 'Confirm Password')
    class Meta:
        model = User
        fields = ['username', 'first_name', 'last_name', 'email', 'phone_number', 'date_of_birth', 'address', 'password', 'password2']

    def create(self, validated_data):
        phone_number = validated_data.pop('phone_number', None)
        date_of_birth = validated_data.pop('date_of_birth', None)
        address = validated_data.pop('address', None)
        password = validated_data.pop('password', None)
        first_name = validated_data.pop('first_name', None)
        last_name = validated_data.pop('last_name', None)

        user = User (
            username = validated_data['username'],
            email = validated_data['email'],
            first_name = first_name,
            last_name = last_name
            )

        user.set_password(password)
        user.save()

        Profile.objects.create(
            user=user,
            date_of_birth=date_of_birth,
            address=address,
            phone_number=phone_number
        )

        return user

views.py

class UserRegistrationView(generics.CreateAPIView):
    serializer_class = UserRegistrationSerializer
    queryset = User.objects.all()

问题解析

1. 错误产生的核心原因

DRF的CreateAPIView在完成对象创建后,会默认将创建好的User实例序列化,并把序列化结果作为响应返回给客户端。你的序列化器基于User模型,但address是Profile模型的字段,User实例本身并不具备address属性,因此序列化阶段尝试从User对象中获取address值时,直接抛出了AttributeError。

2. 为什么phone_number和date_of_birth没报错?

这两个字段在序列化器中设置了required=False,且它们的默认值为None。DRF在序列化字段时,如果字段是非必填且实例没有对应属性,会自动返回None而不会触发错误;但address是必填字段(未设置required=False),DRF会强制尝试从实例中读取该字段的值,找不到对应属性就直接报错。

3. write_only=True解决问题的原理

write_only=True参数告诉DRF:该字段仅用于接收客户端提交的输入数据,不会在序列化输出(即返回响应时)包含此字段。因此序列化返回阶段不会去尝试从User实例中获取address的值,自然也就不会触发AttributeError。

更规范的实现建议

如果希望在响应中同时返回Profile的字段,更合理的做法是:

  • 使用嵌套序列化器:单独编写ProfileSerializer,然后在UserRegistrationSerializer中嵌套该序列化器,通过source关联到profile对象;
  • 重写to_representation方法:在UserRegistrationSerializer中重写to_representation,手动补充Profile的字段数据;
  • 返回组合数据:在create方法中返回包含User和Profile数据的字典(而非仅返回User实例),但这种方式需要调整序列化逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:58:22