Django单元测试IntegrityError求助:重复键违反唯一约束
根据你提供的代码和错误信息,问题出在OneToOne字段的重复关联上,错误提示里的development_developmentproject_project_id_key唯一约束冲突,说明同一个Project被多个DevelopmentProject(或关联模型)绑定了,而OneToOne关系要求一个Project只能对应一个关联对象。下面是具体的排查和解决步骤:
一、先定位核心问题
从错误信息和代码来看,最可能的两个根源:
- 模型定义存在笔误
django-mommy自动创建了额外的关联对象,触发唯一约束
1. 修复模型定义的笔误
看你的Invoice模型代码:
job = models.ForeignKey(DevelopmentProject, related_name = 'job_invoices', blank = True, null = True, on_delete = models.CASCADE)
这里的DevelopmentProject是未在你提供的模型中定义的类,但你测试中创建的是Job模型并关联到Invoice。这很可能是笔误,应该把DevelopmentProject改为你实际的Job模型:
# 修正后(假设Job在development应用下) job = models.ForeignKey('development.Job', related_name='job_invoices', blank=True, null=True, on_delete=models.CASCADE)
如果DevelopmentProject是你实际存在的模型,且它和Project是OneToOne关系,那你需要确保测试中不会重复创建它关联同一个Project。
2. 禁用django-mommy的自动关联创建
django-mommy默认会自动创建所有外键关联的对象,哪怕你已经手动指定了关联,这可能导致重复创建绑定同一个Project的对象,触发OneToOne约束。修改测试中创建Invoice的代码,禁用自动关联创建:
self.invoice = mommy.make( 'development.invoice', job=self.job, Invoice_creator=self.userprofile, # 可选:手动指定必填关联,避免自动创建 _create_relatives=False # 关键:禁用自动创建关联对象 )
3. 简化测试中的对象创建
你当前创建了2个Project,但只用到了第一个,多余的Project可能引发不必要的自动关联。可以改为只创建1个Project:
# 去掉_quantity=2,只创建1个Project self.project = mommy.make('project_profile.Project', municipality=self.municipality[0]) self.job = mommy.make('development.Job', project=self.project) # 无需索引[0]
4. 手动创建对象排查(终极方案)
如果以上方法都无效,放弃django-mommy,手动创建所有对象,彻底控制关联关系,排查冲突点:
def setUp(self): super().setUp() self.factory = RequestFactory() # 手动创建UserProfile(注意metropolitan是必填字段,必须赋值) self.userprofile = UserProfile.objects.create(metropolitan='Test Metro') # 手动创建Municipality self.municipality = Municipality.objects.create(name='Test City', abb='TC', userprofile=self.userprofile) # 手动创建Project self.project = Project.objects.create(name='Test Project', municipality=self.municipality) # 手动创建Job(OneToOne关联Project) self.job = Job.objects.create(project=self.project) # 手动创建Invoice self.invoice = Invoice.objects.create(PO='PO-001', invoice_type='Rental', job=self.job) # 测试URL the_url = reverse('development:update_invoice', args=(self.invoice.pk,)) self.response = self.client.get(the_url, follow=True)
5. 检查数据库迁移和约束
确保你的迁移文件和模型定义一致,执行以下命令刷新数据库约束:
python manage.py makemigrations python manage.py migrate
旧的迁移文件可能导致数据库约束和当前模型不匹配,引发奇怪的冲突。
内容的提问来源于stack exchange,提问作者EarlyCoder

