Django 测试:Postgres 上的 setUpTestData 抛出:“重复的键值违反了唯一约束”

作者:编程家 分类: django 时间:2026-01-01

使用Django 测试:Postgres 上的 setUpTestData 抛出:“重复的键值违反了唯一约束”标题的文章如下:

在进行 Django 开发过程中,我们经常需要编写测试来验证我们的应用程序是否按预期工作。然而,在使用 Postgres 数据库进行测试时,有时会遇到一个常见的错误:“重复的键值违反了唯一约束”。这个错误通常发生在使用 setUpTestData 方法设置测试数据时。

为了更好地理解这个问题,让我们先来了解一下 Django 测试中的 setUpTestData 方法。这个方法是在测试类中用于设置测试数据的一个特殊方法。与其他测试方法不同,setUpTestData 方法只会在测试类中的所有测试方法执行之前运行一次。这意味着我们可以在这个方法中创建一些测试数据,然后在所有测试方法中共享这些数据。

然而,当我们在使用 Postgres 数据库时,可能会遇到一个问题:在调用 setUpTestData 方法时,如果我们创建了两个或更多具有相同唯一约束的对象,那么会抛出一个错误,指示“重复的键值违反了唯一约束”。

这个问题的根本原因是,setUpTestData 方法在实际运行时是在一个事务中执行的。而在 Postgres 数据库中,事务中的操作是在一个独立的隔离环境中执行的,这意味着在事务中创建的对象在事务结束之前对其他事务是不可见的。

为了解决这个问题,我们可以在 setUpTestData 方法中使用 transaction.atomic 装饰器来确保在事务中运行的代码不会抛出异常。这样,如果创建对象时发生了重复键值的情况,事务将自动回滚,从而避免了唯一约束错误的发生。

下面是一个示例代码,演示了如何在 Django 测试中使用 setUpTestData 方法,并解决“重复的键值违反了唯一约束”的问题:

from django.test import TestCase

from django.db import transaction

from myapp.models import MyModel

class MyModelTestCase(TestCase):

@classmethod

def setUpTestData(cls):

with transaction.atomic():

for i in range(3):

MyModel.objects.create(name=f"Test {i}")

def test_unique_constraint(self):

with self.assertRaises(MyModel.DoesNotExist):

MyModel.objects.create(name="Test 0")

在上面的示例代码中,我们使用了 transaction.atomic 装饰器将 setUpTestData 方法中的代码包装在一个事务中。通过这样做,我们确保了在事务中创建的对象不会引发重复键值的异常。在测试方法 test_unique_constraint 中,我们验证了当我们尝试创建一个已经存在的对象时,是否会抛出 MyModel.DoesNotExist 异常。

通过以上的解决方案,我们可以在使用 Postgres 数据库进行 Django 测试时,成功地避免“重复的键值违反了唯一约束”的错误。这样,我们可以更加轻松地编写和执行测试,确保我们的应用程序在各种情况下都能正常工作。