Django DRF多类型用户注册报错:user_id列违反非空约束
问题根源与解决方案
让我帮你拆解这个问题,你遇到的null value in column "user_id"错误核心原因是对象创建顺序颠倒+序列化器字段映射错误,具体来看:
问题根源分析
- 外键依赖顺序搞反了:Participant的
user是OneToOne外键,必须关联一个已存在的AbstractUser实例才能创建。但你现在的序列化器create方法里,先尝试创建Participant,再去创建AbstractUser,这时候Participant的user_id自然是空的,触发非空约束错误。 - 序列化器字段名错误:Participant模型里的外键是
user,而你在序列化器里用了participants——这是你给OneToOneField设置的related_name,这个名称是给AbstractUser模型反向查询Participant用的,不是Participant本身的字段,所以字段映射完全错了。 - 不必要的循环处理:
participants_data是单个用户的数据(因为是OneToOne关联),你用for participant_data in participants_data循环处理会把字典拆成单个键值对,完全不符合逻辑。
修正步骤
1. 修正序列化器字段与create逻辑
把ParticipantSerializer里的字段改成user,并且调整创建顺序:先创建AbstractUser,再用这个user实例创建Participant。
修改后的serializers.py:
class AbstractUserSerializer(serializers.ModelSerializer): class Meta: model = AbstractUser fields = ('id', 'phone_number', 'email', 'password') # 添加write_only,避免返回明文密码 extra_kwargs = {'password': {'write_only': True}} class ParticipantSerializer(serializers.ModelSerializer): # 这里改成user,对应Participant模型的外键字段 user = AbstractUserSerializer() class Meta: model = Participant fields = ('user', 'first_name', 'last_name', 'device_reg_token') def create(self, validated_data): # 先取出用户数据,创建AbstractUser实例 user_data = validated_data.pop('user') # 注意:如果你的AbstractUser有自定义的create_user方法,要用它来创建 user = AbstractUser.objects.create_user(**user_data) # 用创建好的user实例来创建Participant participant = Participant.objects.create(user=user, **validated_data) return participant
2. 确保AbstractUser有正确的create_user方法
如果你的AbstractUser没有自定义create_user方法,需要补充一个(因为你继承了AbstractBaseUser),比如在AbstractUser模型里添加:
from django.contrib.auth.base_user import BaseUserManager class AbstractUser(AbstractBaseUser, PermissionsMixin): phone_number = models.CharField( _('phone number'), max_length=14, unique=True, help_text='Enter your phone number', ) email = models.EmailField(_('email address'), blank=True) created = models.DateTimeField(_('date joined'), default=timezone.now) # 指定USERNAME_FIELD,AbstractBaseUser必须设置 USERNAME_FIELD = 'phone_number' REQUIRED_FIELDS = [] objects = UserManager() # 自定义用户管理器 class UserManager(BaseUserManager): def create_user(self, phone_number, password=None, **extra_fields): if not phone_number: raise ValueError('The Phone Number must be set') user = self.model(phone_number=phone_number, **extra_fields) user.set_password(password) user.save(using=self._db) return user
3. 修正视图的perform_create(如果有自定义)
从错误栈看,你的CreateUserView里重写了perform_create,传入了participants=self.request.user,但注册时用户还未登录,request.user是匿名用户,这完全没必要,应该删掉这个自定义的perform_create,让默认逻辑处理:
修改后的views.py:
class CreateUserView(generics.CreateAPIView): queryset = Participant.objects.all() permission_classes = [ permissions.AllowAny ] serializer_class = ParticipantSerializer # 删掉这个错误的自定义方法 # def perform_create(self, serializer): # serializer.save(participants=self.request.user)
测试请求示例
现在你可以发送这样的POST请求来测试:
{ "user": { "phone_number": "1234567890", "email": "test@example.com", "password": "yourpassword123" }, "first_name": "Kabiljan", "last_name": "Tanaguzov", "device_reg_token": "test_token_123" }
这样就能正确创建关联的AbstractUser和Participant实例,不会再触发user_id为空的错误了。
内容的提问来源于stack exchange,提问作者Kabiljan Tanaguzov
相关产品推荐
相关产品推荐

