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

Django自定义用户模型创建Driver时唯一键约束冲突求助

问题分析与解决方案

核心错误原因

  • Driver表空邮箱冲突:post_save信号创建Driver时硬编码了email = "",而Driver的email字段设为unique=True,第一次创建后空邮箱已存在,后续创建直接触发唯一约束错误。
  • CustomUser邮箱无唯一校验:默认AbstractUser的email不是唯一字段,导致可以创建重复邮箱的CustomUser,但Driver的email要求唯一,两者产生冲突。
  • 冗余字段增加风险:Driver中重复存储email字段,既没必要(可通过关联的CustomUser获取),又增加了数据不一致和冲突的概率。

分步解决步骤

1. 修复post_save信号的硬编码空邮箱

修改Models.py中的信号接收器,改用CustomUser的email填充Driver的email字段,避免空值重复:

@receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance, created, **kwargs):
    if created and instance.is_driver:
        Driver.objects.create(
            driver_user=instance, 
            full_name="123",
            phone_number="123",
            email=instance.email,  # 关联User的邮箱,避免空值
            address="123",
            country="123",
            state="123",
            city="123",
            zipp="213",
            birth_date='2022-01-01',
            license_no=1234,  # 注意IntegerField不能传字符串
            license_exp_date='2022-01-01',
            last_medical='2022-01-01',
            next_medical='2022-01-01',
            last_drug_test='2022-01-01',
            next_drug_test='2022-01-01',
        )

2. 给CustomUser添加邮箱唯一约束

修改CustomUser模型,确保email字段唯一,从根源避免重复用户:

class CustomUser(AbstractUser):
    is_driver = models.BooleanField(default=False)
    is_accountant = models.BooleanField(default=False)
    is_dispatcher = models.BooleanField(default=False)
    email = models.EmailField(unique=True, max_length=255)  # 添加唯一约束

执行数据库迁移:

python manage.py makemigrations
python manage.py migrate

3. 移除Driver中冗余的email字段(推荐)

由于Driver和CustomUser是一对一关联,完全可以通过driver.driver_user.email获取邮箱,删除重复字段减少维护成本:

class Driver(models.Model):
    driver_user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, primary_key=True)
    full_name = models.CharField(max_length=50)
    phone_number = models.CharField(max_length=50)
    # 移除email字段
    address = models.CharField(max_length=70)
    country = models.CharField(max_length=50)
    state = models.CharField(choices=US_STATES,max_length=50)
    city = models.CharField(max_length=50)
    zipp = models.CharField(max_length=50)
    birth_date = models.DateField()
    license_no = models.IntegerField()
    license_exp_date = models.DateField()
    last_medical = models.DateField(blank=True, null=True)
    next_medical = models.DateField(blank=True, null=True)
    last_drug_test = models.DateField(blank=True, null=True)
    next_drug_test = models.DateField(blank=True, null=True)
    date_created = models.DateTimeField(auto_now_add=True)
    date_edited = models.DateTimeField(auto_now=True)
    status = models.IntegerField(choices=STATUS, default=1)

执行迁移更新数据库。

4. 优化视图逻辑,提前校验+正确保存

在创建用户前先检查邮箱是否已存在,更新Driver时直接保存Driver实例:

def create_driver_view(request):
    if request.method == "POST":
        add_driver = DriverForm(request.POST)
        add_driver_file = request.FILES.getlist("file")
        
        if add_driver.is_valid():
            password = 'Ga20224$5%'
            cleaned_data = add_driver.cleaned_data
            email = cleaned_data['email']
            
            # 提前检查邮箱是否已注册
            if CustomUser.objects.filter(email=email).exists():
                add_driver.add_error('email', '该邮箱已被注册')
                return render(request, "drivers/add.html", {
                    "add_driver": add_driver, 
                    "add_driver_files": DriverFormUpload()
                })
            
            username = email.split('@')[0] + uuid.uuid4().hex[:2]
            user = CustomUser.objects.create_user(
                username=username, 
                password=password, 
                is_driver=True, 
                email=email
            )
            
            # 更新Driver并保存
            driver = user.driver
            driver.full_name = cleaned_data['full_name']
            driver.phone_number = cleaned_data['phone_number']
            driver.address = cleaned_data['address']
            driver.country = cleaned_data['country']
            driver.state = cleaned_data['state']
            driver.city = cleaned_data['city']
            driver.zipp = cleaned_data['zipp']
            driver.birth_date = cleaned_data['birth_date']
            driver.license_no = cleaned_data['license_no']
            driver.license_exp_date = cleaned_data['license_exp_date']
            driver.last_medical = cleaned_data['last_medical']
            driver.next_medical = cleaned_data['next_medical']
            driver.last_drug_test = cleaned_data['last_drug_test']
            driver.next_drug_test = cleaned_data['next_drug_test']
            driver.save()
            
            # 处理文件上传
            for file in add_driver_file:
                DriversFiles.objects.create(driver_files=driver, file=file)
            
            return redirect('drivers:list_driver')  
        else:
            print(add_driver.errors)
            
    else:
        add_driver = DriverForm()
        add_driver_files = DriverFormUpload()

    return render(request, "drivers/add.html", {"add_driver": add_driver, "add_driver_files": add_driver_files})

自定义用户模型最佳实践

  • 必须给email字段添加unique=True,这是用户标识和登录的常用字段
  • 扩展模型(如Driver)不要重复存储User中已有的字段,通过关联关系获取即可
  • 使用post_save信号自动创建扩展模型时,避免硬编码不合理的默认值(如空字符串)
  • 创建用户前主动校验唯一字段,提前返回错误提示,避免触发数据库约束错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 17:50:36