如何在Django REST Framework中通过嵌套序列化器实现创建用户配置文件时自动创建用户?(解决TypeError报错)
问题分析
你遇到的TypeError: User() got an unexpected keyword argument 'user'主要有两个核心原因:
- ProfileAdmin模型错误继承User:你的
ProfileAdmin同时继承了User和models.Model,这会导致Django在创建ProfileAdmin时,错误地将user参数传递给User的构造函数,但User模型并没有这个字段。 - 序列化器create方法逻辑顺序错误:你先创建了ProfileAdmin对象,再创建User对象,但ProfileAdmin依赖User的OneToOne关联,顺序应该反过来,并且没有正确关联两者。
分步解决方案
1. 修复ProfileAdmin模型定义
打开models.py,修改ProfileAdmin的继承关系,移除对User的继承——它只需要作为User的扩展信息存在,通过OneToOneField关联即可:
# 替换原来的ProfileAdmin定义 class ProfileAdmin(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True) first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) email = models.EmailField(verbose_name="email", max_length=85, unique=True) aliasMailID = models.EmailField(verbose_name="aliasMailID", max_length=85, unique=True, blank=False) address = models.TextField() MSteamsID = models.EmailField(verbose_name="MSteamsID", max_length=85, unique=True, blank=False) telNum = PhoneNumberField(null=False, blank=False, unique=True)
同时,删除User模型中重复定义的password字段——因为AbstractBaseUser已经包含了这个字段,重复定义会导致字段冲突:
class User(AbstractBaseUser, PermissionsMixin): # 移除下面这行重复定义的密码字段 # password = models.CharField(_('password'), max_length=128) # ... 其他字段保持不变
2. 修正序列化器的create和update方法
打开serializers.py,调整UserProfileSerializer的逻辑:先创建User对象(用自定义管理器的方法处理密码哈希),再关联创建ProfileAdmin,同时完善更新逻辑:
class UserProfileSerializer(serializers.ModelSerializer): user_account = UserSerializer(many=False) class Meta: model = ProfileAdmin fields = ['email', 'first_name', 'last_name', 'address', 'telNum', 'aliasMailID', 'MSteamsID', 'user_account'] def create(self, validated_data): account_data = validated_data.pop('user_account') # 使用自定义管理器的create_user方法创建User,自动处理密码哈希 user = User.objects.create_user( username=account_data['username'], password=account_data['password'], user_type=account_data['user_type'] ) # 创建ProfileAdmin并关联刚创建的User profile = ProfileAdmin.objects.create(user=user, **validated_data) return profile def update(self, instance, validated_data): account_data = validated_data.pop('user_account') user_account = instance.user # 更新ProfileAdmin字段 instance.email = validated_data.get('email', instance.email) instance.first_name = validated_data.get('first_name', instance.first_name) instance.last_name = validated_data.get('last_name', instance.last_name) instance.address = validated_data.get('address', instance.address) instance.telNum = validated_data.get('telNum', instance.telNum) instance.aliasMailID = validated_data.get('aliasMailID', instance.aliasMailID) instance.MSteamsID = validated_data.get('MSteamsID', instance.MSteamsID) instance.save() # 更新User字段 user_account.username = account_data.get('username', user_account.username) user_account.user_type = account_data.get('user_type', user_account.user_type) # 单独处理密码更新,确保哈希加密 if 'password' in account_data: user_account.set_password(account_data['password']) user_account.save() return instance
3. 扩展支持多角色Profile(可选)
如果你需要支持Instructor和Student的Profile,可以参考ProfileAdmin创建对应的ProfileInstructor和ProfileStudent模型,然后编写一个动态序列化器,根据user_type自动匹配对应的Profile序列化器:
class DynamicProfileSerializer(serializers.Serializer): def to_representation(self, instance): user_type = instance.user.user_type if user_type == 'ADMIN': return UserProfileSerializer(instance).data elif user_type == 'INSTRUCTOR': return InstructorProfileSerializer(instance).data elif user_type == 'STUDENT': return StudentProfileSerializer(instance).data return super().to_representation(instance)
4. 数据库迁移
修改模型后,执行以下命令更新数据库结构:
python manage.py makemigrations python manage.py migrate
测试验证
重新运行服务后,访问http://localhost:8000/api/users/,提交如下格式的JSON请求即可正常创建关联的User和ProfileAdmin:
{ "email": "admin@example.com", "first_name": "John", "last_name": "Doe", "address": "123 Street", "telNum": "+1234567890", "aliasMailID": "admin_alias@example.com", "MSteamsID": "admin_teams@example.com", "user_account": { "username": "admin_user", "password": "admin123", "user_type": "ADMIN" } }
内容的提问来源于stack exchange,提问作者Ravi Trivedi
相关产品推荐
相关产品推荐

