当前位置: 首页 > 知识库问答 >
问题:

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

党宇定
2023-03-14

我在单元测试中遇到了一个数据库问题。我认为这与我使用TestCase和setUpData的方式有关。

当我尝试使用某些值设置测试数据时,测试会抛出以下错误:

django.db.utils.IntegrityError: duplicate key value violates unique constraint 

...

psycopg2.IntegrityError: duplicate key value violates unique constraint "InventoryLogs_productgroup_product_name_48ec6f8d_uniq"
DETAIL:  Key (product_name)=(Almonds) already exists.

我更改了所有主键,它似乎运行正常。它似乎不会影响任何测试。

但是,我担心我做错了什么。当它第一次发生时,我在我的应用程序上逆转了大约一个小时的工作(对于新手来说,代码不多),这纠正了这个问题。

然后,当我写回更改时,同样的问题再次出现。TestCase粘贴在下面。问题似乎发生在我添加sortrecord项之后,但与上面的项相对应。

我不想在我的测试中继续浏览和更改主键和URL,所以如果有人发现我使用它的方式有问题,请帮助我。谢谢!

测试用例

class DetailsPageTest(TestCase):


@classmethod
def setUpTestData(cls):

    cls.product1 = ProductGroup.objects.create(
                        product_name="Almonds"
                        )
    cls.variety1 = Variety.objects.create(
                        product_group = cls.product1,
                        variety_name = "non pareil",
                        husked = False,
                        finished = False,
                        )

    cls.supplier1 = Supplier.objects.create(
                        company_name = "Acme",
                        company_location = "Acme Acres",
                        contact_info = "Call me!"
                        )

    cls.shipment1 = Purchase.objects.create(
                        tag=9,
                        shipment_id=9999,
                        supplier_id = cls.supplier1,
                        purchase_date='2015-01-09',
                        purchase_price=9.99,
                        product_name=cls.variety1,
                        pieces=99,
                        kgs=999,
                        crackout_estimate=99.9
                        )
    cls.shipment2 = Purchase.objects.create(
                        tag=8,
                        shipment_id=8888,
                        supplier_id=cls.supplier1,
                        purchase_date='2015-01-08',
                        purchase_price=8.88,
                        product_name=cls.variety1,
                        pieces=88,
                        kgs=888,
                        crackout_estimate=88.8
                        )
    cls.shipment3 = Purchase.objects.create(
                        tag=7,
                        shipment_id=7777,
                        supplier_id=cls.supplier1,
                        purchase_date='2014-01-07',
                        purchase_price=7.77,
                        product_name=cls.variety1,
                        pieces=77,
                        kgs=777,
                        crackout_estimate=77.7
                        )

    cls.sortrecord1 = SortingRecords.objects.create(
                        tag=cls.shipment1,
                        date="2015-02-05",
                        bags_sorted=20,
                        turnout=199,
                        )

    cls.sortrecord2 = SortingRecords.objects.create(
                        tag=cls.shipment1,
                        date="2015-02-07",
                        bags_sorted=40,
                        turnout=399,
                        )
    cls.sortrecord3 = SortingRecords.objects.create(
                        tag=cls.shipment1,
                        date='2015-02-09',
                        bags_sorted=30,
                        turnout=299,
                        )

模型

from datetime import datetime

from django.db import models
from django.db.models import Q


class ProductGroup(models.Model):
    product_name = models.CharField(max_length=140, primary_key=True)

    def __str__(self):
        return self.product_name

    class Meta:
        verbose_name = "Product"

class Supplier(models.Model):
    company_name = models.CharField(max_length=45)
    company_location = models.CharField(max_length=45)
    contact_info = models.CharField(max_length=256)

    class Meta:
        ordering = ["company_name"]

    def __str__(self):
        return self.company_name

class Variety(models.Model):
    product_group = models.ForeignKey(ProductGroup)
    variety_name = models.CharField(max_length=140)
    husked = models.BooleanField()
    finished = models.BooleanField()
    description = models.CharField(max_length=500, blank=True)

    class Meta:
        ordering = ["product_group_id"]
        verbose_name_plural = "Varieties"

    def __str__(self):
        return self.variety_name


class PurchaseYears(models.Manager):

    def purchase_years_list(self):
        unique_years = Purchase.objects.dates('purchase_date', 'year')
        results_list = []
        for p in unique_years:
            results_list.append(p.year)
        return results_list


class Purchase(models.Model):
    tag = models.IntegerField(primary_key=True)
    product_name = models.ForeignKey(Variety, related_name='purchases')
    shipment_id = models.CharField(max_length=24)
    supplier_id = models.ForeignKey(Supplier)
    purchase_date = models.DateField()
    estimated_delivery = models.DateField(null=True, blank=True)
    purchase_price = models.DecimalField(max_digits=6, decimal_places=3)
    pieces = models.IntegerField()
    kgs = models.IntegerField()
    crackout_estimate = models.DecimalField(max_digits=6,decimal_places=3, null=True)
    crackout_actual = models.DecimalField(max_digits=6,decimal_places=3, null=True)
    objects = models.Manager()
    purchase_years = PurchaseYears()
    # Keep manager as "objects" in case admin, etc. needs it. Filter can be called like so:
    # Purchase.objects.purchase_years_list()
    # Managers in docs: https://docs.djangoproject.com/en/1.8/intro/tutorial01/

    class Meta:
        ordering = ["purchase_date"]

    def __str__(self):
        return self.shipment_id

    def _weight_conversion(self):
        return round(self.kgs * 2.20462)
    lbs = property(_weight_conversion)

class SortingModelsBagsCalulator(models.Manager):

    def total_sorted(self, record_date, current_set):
        sorted = [SortingRecords['bags_sorted'] for SortingRecords in current_set if
                  SortingRecords['date'] <= record_date]
        return sum(sorted)


class SortingRecords(models.Model):
    tag = models.ForeignKey(Purchase, related_name='sorting_record')
    date = models.DateField()
    bags_sorted = models.IntegerField()
    turnout = models.IntegerField()
    objects = models.Manager()

    def __str__(self):
        return "%s  [%s]" % (self.date, self.tag.tag)

    class Meta:
        ordering = ["date"]
        verbose_name_plural = "Sorting Records"

    def _calculate_kgs_sorted(self):
        kg_per_bag = self.tag.kgs / self.tag.pieces
        kgs_sorted = kg_per_bag * self.bags_sorted
        return (round(kgs_sorted, 2))
    kgs_sorted = property(_calculate_kgs_sorted)

    def _byproduct(self):
        waste = self.kgs_sorted - self.turnout
        return  (round(waste, 2))
    byproduct = property(_byproduct)

    def _bags_remaining(self):
        current_set = SortingRecords.objects.values().filter(~Q(id=self.id), tag=self.tag)
        sorted = [SortingRecords['bags_sorted'] for SortingRecords in current_set if
                  SortingRecords['date'] <= self.date]
        remaining = self.tag.pieces - sum(sorted) - self.bags_sorted
        return remaining
    bags_remaining = property(_bags_remaining)

编辑

对于整数,它也会失败,就像这样。

django.db.utils.IntegrityError: duplicate key value violates unique constraint "InventoryLogs_purchase_pkey"
DETAIL:  Key (tag)=(9) already exists.

优迪帕特

所以我应该早点提到这个,但是我完全忘记了。我有两个使用相同数据的单元测试文件。为了好玩,我将< code>setUpTestData()的两个实例中的主键匹配到不同的值,果然,我得到了相同的错误。

在我向其中一个添加更多数据之前,这两个设置并排工作得很好。现在,它们似乎需要不同的值。我猜你只能在这么长时间内使用重复数据。

共有3个答案

慕容晔
2023-03-14

您提供的日志显示详细信息:密钥 (product_name)=(杏仁)已存在。您是否在数据库中进行了验证?

为了防止将来出现此类错误,您应该在所有测试数据字符串前面加上test_

穆展鹏
2023-03-14

几个月来,我一直在偶尔遇到这个问题。我相信我刚刚找出了根本原因和几个解决方案。

总结 无论出于何种原因,Django测试用例基类似乎都没有删除由我们在运行TestCase2之前将其称为TestCase1创建的数据库记录。在 TestCase2 中,当它尝试使用与 TestCase1 相同的 ID 在数据库中创建记录时,数据库将引发重复密钥异常,因为这些 ID 已存在于数据库中。即使说“请”这个神奇的词也无助于数据库重复的按键错误。

好消息是,有多种方法可以解决这个问题!这里有一对夫妇...

解决方案 1

确保是否重写了类方法拆解类,您称之为超级().teardown类()。如果你在不调用其超级的情况下覆盖了 tearDownClass(),那么它永远不会调用TransactionTestCase._post_teardown()TransactionTestCase._fixture_teardown()。引用TransactionTestCase._post_teardown()'中的文档字符串:

    def _post_teardown(self):
        """
        Perform post-test things:
        * Flush the contents of the database to leave a clean slate. If the
          class has an 'available_apps' attribute, don't fire post_migrate.
        * Force-close the connection so the next test gets a clean cursor.
        """

如果<code>测试用例。如果不通过super()调用tearDownClass(),则在测试用例之间不会重置数据库,您将得到可怕的重复键异常。

解决方案 2

重写事务测试用例并将类变量设置为 serialized_rollback = True,如下所示:

class MyTestCase(TransactionTestCase):
    fixtures = ['test-data.json']
    serialized_rollback = True

    def test_name_goes_here(self):
        pass

引用来源:

class TransactionTestCase(SimpleTestCase):

    ...

    # If transactions aren't available, Django will serialize the database
    # contents into a fixture during setup and flush and reload them
    # during teardown (as flush does not restore data from migrations).
    # This can be slow; this flag allows enabling on a per-case basis.
    serialized_rollback = False

serialized_rollback设置为True时,Django测试运行程序会在测试用例之前回滚插入到数据库中的任何事务。巴塔冰,巴塔砰……不再有重复的键错误!

结论

可能有更多的方法来实现OP问题的解决方案,但是这两种方法应该可以很好地工作。为了清晰起见和对底层Django测试用例基类的更深理解,我肯定会喜欢其他人添加更多的解决方案。唷,把最后一句话说三遍,你就能赢得一匹小马!

欧阳勇
2023-03-14

我继续收到此错误,没有任何重复的数据,但我能够通过初始化对象并调用 save() 方法而不是通过 Model.objects.create() 创建对象来解决此问题

换句话说,我是这样做的:

@classmethod
def setUpTestData(cls):
    cls.person = Person(first_name="Jane", last_name="Doe")
    cls.person.save()

取而代之的是:

@classmethod
def setUpTestData(cls):
    cls.person = Person.objects.create(first_name="Jane", last_name="Doe")
 类似资料:
  • 问题内容: 我正在跟着我先前提出的一个问题,在这个问题中,我试图寻求从愚蠢/编写不佳的mysql查询到postgresql的转换。我相信我成功了。无论如何,我正在使用从mysql数据库手动移动到postgres数据库的数据。我正在使用如下查询: 我有理由相信这很好。但是,这导致了新问题。尝试提交时,我从django收到一条错误,指出: 我已经看过这里发布的一些回复,但是我还没有找到解决我的问题的方

  • 我在创建应用程序时遇到了这个问题。因此,每当我添加第一条评论时,问题都不会出现,但当我第二次尝试时,我会收到此错误: 重复的键值违反了唯一约束“tripplanner_discussion_author_id_key”详细信息:键 (author_id)=(1) 已存在。 我试图把放到 models.py,但它根本没有帮助。 models.py views.py 更新 当我登录到另一个用户时,一个

  • 我有一个笑话模型: 现在,当我试图迁移最后一行时,我得到了错误。基本上,我想将一个用户链接到Joke对象,因为我已经有了一个数据库,所以我希望默认值为1,这是管理员用户的id(我检查过了...).Makemigrations工作正常,但是当我尝试迁移时,我得到了这个: 我真的不明白怎么了。有什么想法吗?

  • 在我从事的一个项目中,这个问题发生过几次,尽管我使用了我在这个网站上找到的“python manage . py sqlsequencereset”修复程序,它工作了一段时间,然后又开始抛出错误。 我一直得到的错误非常简单: 在我看来,我有: 我现在已经做了几次sqlequence重置,然后错误在几周后返回。我没有导入任何数据,但我的同事正在现场使用他们的手机创建新对象,一次一个。 运行sqlse

  • 我有这样的桌子: 我试图插入一些查询到另一个表(device_usage_test1)。这是我的表: 这是我的插入命令: 稍后,我将在device_usage_test1上创建一个插入查询。所以我的设备序列必须是唯一的。但是当我尝试用deviceserial (unique)插入时。它显示错误:< code >错误:重复的键值违反了唯一约束“device _ usage _ device seri

  • 当并发客户机试图将数据插入子表时,我们面临唯一的约束冲突问题。 假设我们有1以下的表格。用户user_id、first_name、last_name。2.项目project_idproject_name和project_description。 两者都有着多对多的关系。 当两个客户端试图创建一个新用户时。假设client1创建了user1(id=aa1),子记录项目(id=1)。Client2还创