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

DRF中关联User与UserProfile序列化时遇AttributeError问题求助

问题原因

报错AttributeError: 'User' instance has no attribute 'user'的本质是:你试图用操作User模型的视图来处理包含嵌套UserSerializer的UserProfileSerializer,而User实例本身并没有名为user的属性——这个属性属于UserProfile模型,用于关联User。同时,嵌套序列化器默认不支持自动创建关联对象,需要手动处理创建逻辑。

修复方案

1. 完善UserSerializer的密码处理

Django的用户密码需要通过set_password方法加密,不能直接存入数据库,所以重写UserSerializer的create方法:

class UserSerializer(serializers.ModelSerializer):
    # 保留原字段定义
    first_name = serializers.CharField(required=True)
    last_name = serializers.CharField(required=True)
    registration_data = serializers.DateTimeField(
        read_only=True, default=serializers.CreateOnlyDefault(datetime.now())
    )
    password = serializers.CharField(
        required=True,
        write_only=True,
        max_length=128,
        error_messages={
            'required': 'Please enter a password.',
            'max_length': 'Password should be no more than 128 characters.'
        },
        validators=[
            validators.MinLengthValidator(8, message='Password must be at least 8 characters.'),
            validators.RegexValidator(
                regex=r'[A-Z]',
                message='Password must contain at least one uppercase letter.',
                code='invalid_password'
            ),
            validators.RegexValidator(
                regex=r'[a-z]',
                message='Password must contain at least one lowercase letter.',
                code='invalid_password'
            ),
            validators.RegexValidator(
                regex=r'[0-9]',
                message='Password must contain at least one number.',
                code='invalid_password'
            ),
            validators.RegexValidator(
                regex=r'[!@#$%^&*()]',
                message='Password must contain at least one special character.',
                code='invalid_password'
            )
        ]
    )

    class Meta:
        model = User
        exclude = ['id']

    def create(self, validated_data):
        # 提取密码并加密
        password = validated_data.pop('password')
        user = User(**validated_data)
        user.set_password(password)
        user.save()
        return user

2. 重写UserProfileSerializer的创建逻辑

嵌套序列化器不会自动创建关联的User实例,需要手动拆分数据并创建两个模型实例:

class UserProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer(required=True)

    class Meta:
        model = UserProfile
        fields = ('user', 'phone', 'phone_verified', 'email_verified', 'phone_code', 'email_code')

    def create(self, validated_data):
        # 分离嵌套的用户数据
        user_data = validated_data.pop('user')
        # 创建User实例
        user = UserSerializer.create(UserSerializer(), validated_data=user_data)
        # 创建关联的UserProfile实例
        profile = UserProfile.objects.create(user=user, **validated_data)
        return profile

3. 调整视图配置

将视图的序列化器改为UserProfileSerializer,确保操作对象匹配:

class RegisterUserView(generics.CreateAPIView):
    queryset = UserProfile.objects.all()
    serializer_class = UserProfileSerializer

4. 正确的请求格式示例

注册时需要传递嵌套的用户信息,JSON格式如下:

{
    "user": {
        "username": "demo_user",
        "first_name": "Demo",
        "last_name": "User",
        "email": "demo@example.com",
        "password": "DemoPass123!"
    },
    "phone": "13800138000",
    "phone_code": "123456",
    "email_code": "654321"
}
额外优化建议
  • 给UserProfile的user字段添加related_name:user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile'),这样可以通过user.profile快速访问用户的扩展信息。
  • phone_verified和email_verified已经设置默认值,可以从请求字段中移除,序列化器会自动使用默认值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 22:05:25