【问题标题】:Django - "psycopg2.errors.ForeignKeyViolation: insert or update on table" when running "python manage.py test", but not when running single testDjango - 运行“python manage.py test”时“psycopg2.errors.ForeignKeyViolation:在表上插入或更新”,但在运行单个测试时不会
【发布时间】:2021-12-06 00:30:53
【问题描述】:

所以我就这个issue 发表了另一篇文章,并认为我会进一步分解它,看看 StackOverflow 的好人是否能弄清楚发生了什么,因为我不能。所以我在下面使用setUpTestData 生成了所有这些测试数据,看起来不错。我将调试器日志放在下面以显示测试数据库填充了预期的内容。当我到达测试test_correct_num_posts_generated 时,测试本身通过了,但我得到了这个奇怪的数据库错误。我把完整的错误输出放在下面。我已经通过许多测试得到了这个。我听说这可能与测试数据库未正确拆除的 Django 错误有关。这是一个奇怪的问题,因为当我运行 python manage.py test cheers.test.ExploreTest 时它通过了,而 python manage.py test cheers.test.ExploreTest.test_correct_num_posts_generated 它通过了,但是当我运行 python manage.py test 时,我得到了如下所示的错误。怎么回事?

Test.pyPostFactory 是一个虚拟数据生成器。

@classmethod
    def setUpTestData(cls) -> None:
        cls.num_posts = 45
        cls.health_cate = 'Health'
        cls.fitness_cate = 'Fitness'
        cls.relationship_cate = 'Relationship'
        cls.goal_categories_name_list = [cls.health_cate, cls.fitness_cate, cls.relationship_cate]

        cls.user = create_test_user_in_DB()
        cls.access_token = get_test_user_access_token()
        for name in cls.goal_categories_name_list:
            goal_category_obj = GoalCategory.objects.create(name=name, emoji='Some URL')
            goal_obj = Goal.objects.create(creator=cls.user, goal_category=goal_category_obj)
            if name == cls.relationship_cate:
                cls.relationship_join_goal = JoinGoal.objects.create(joiner=cls.user, goal=goal_obj,
                                                                     status=GoalStatus.ONGOING)
            else:
                JoinGoal.objects.create(joiner=cls.user, goal=goal_obj, status=GoalStatus.ONGOING)

        for i in range(cls.num_posts):
            PostFactory()

调试器在setUpTestData 的末尾打印出来。当我到达test_correct_num_posts_generated 时,这一切都会被打印出来。 >>> 是我输入的行,下面是打印出来的。

>>>User.objects.all()
<QuerySet [<User 6badb4b8-33ba-4bb9-aa9a-2e3afb359960>]>
>>>GoalCategory.objects.all()
<QuerySet [<GoalCategory: GoalCategory object (Health)>, <GoalCategory: GoalCategory object (Fitness)>, <GoalCategory: GoalCategory object (Relationship)>]>
>>>len(Post.objects.all())
45
>>>JoinGoal.objects.all()
<QuerySet [<JoinGoal e7c4e4fa-3592-4ad9-b93a-245694a5e384>, <JoinGoal 7d637523-67db-4c59-aea6-37a8b43f0dd3>, <JoinGoal cbb954b7-332e-47f4-82e6-d2c77d874be5>]>

Test.py 测试失败

def test_correct_num_posts_generated(self):
    self.assertTrue(len(Post.objects.all()), self.num_posts)

self.assertTrue(len(Post.objects.all()), self.num_posts) 通过了,但是这个测试抛出了我提到的臭名昭著的错误:

======================================================================
ERROR: test_correct_num_posts_generated (cheers.test.ExploreTests.ExploreFeedTest.ExploreFeedTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\utils.py", line 82, in _execute
    return self.cursor.execute(sql)
psycopg2.errors.ForeignKeyViolation: insert or update on table "cheers_post" violates foreign key constraint "cheers_post_join_goal_id_da1e6957_fk_cheers_joingoal_uuid"
DETAIL:  Key (join_goal_id)=(5c05a7d2-ff3c-4f1e-95d9-277ae164dabb) is not present in table "cheers_joingoal".


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\test\testcases.py", line 284, in _setup_and_call
    self._post_teardown()
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\test\testcases.py", line 1006, in _post_teardown
    self._fixture_teardown()
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\test\testcases.py", line 1248, in _fixture_teardown
    connections[db_name].check_constraints()
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\postgresql\base.py", line 285, in check_constraints
    cursor.execute('SET CONSTRAINTS ALL IMMEDIATE')
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\utils.py", line 82, in _execute
    return self.cursor.execute(sql)
django.db.utils.IntegrityError: insert or update on table "cheers_post" violates foreign key constraint "cheers_post_join_goal_id_da1e6957_fk_cheers_joingoal_uuid"
DETAIL:  Key (join_goal_id)=(5c05a7d2-ff3c-4f1e-95d9-277ae164dabb) is not present in table "cheers_joingoal".

【问题讨论】:

  • 你能通过 github repo 提供一个可重现的例子吗?
  • @LordElrond 不幸的是,GitHub 存储库是私有的,它需要访问 AWS 服务器,因此有密钥
  • 您已经尝试将所有setUpTestData 移动到setUp(没有@classmethod),对吗?
  • 你能把测试集缩小到 2-3 个单独通过但一起失败的测试吗?我将首先逐个注释掉测试,直到整个套件通过,然后您找到至少 1 个有问题的测试。
  • 是的,所以我有一个名为 Post 的模型。如果我在测试套件中有多个在该表上运行的 TestCase,则只有影响 Post 的第一个 TestCase 通过其他所有失败。

标签: python django


【解决方案1】:

由于test cheers.test.ExploreTest 上的测试通过但test 上没有通过,肯定有来自其他包的一些测试弄乱了数据。

不确定这是否会解决它,但由于您使用的是 Django 3.2,所以docs 中有一个小注释:

在 Django 3.2 中更改:

在 setUpTestData() 中分配给类属性的对象必须支持 使用 copy.deepcopy() 创建深层副本以隔离它们 从每个测试方法进行的更改。在以前的版本中 在 Django 中,这些对象被重用,并且对它们所做的更改是 在测试方法之间持续存在。

使用copy.deepcopy() 复制模型实例无法按预期工作,需要额外的步骤来创建新实例。我在文档中的任何地方都找不到它,但我多次使用来自this answer 的代码(并且它适用于带有 Postgres 的 Django 3.2):

from copy import deepcopy
old_obj = deepcopy(obj)
old_obj.id = None
old_obj.save()

如果是这种情况,我会选择 setUp 而不是 setUpTestData,因为它会在每次测试之前重新创建测试数据库。
它速度较慢,您可能需要更改某些测试的特定数据,但它工作得很好并且更易于维护。

【讨论】:

  • 我之前试过setUp,但我不知道这个deepcopy我去看看
  • 我也没有尝试从以前的测试用例创建数据副本。
  • setUp 不幸地给出了同样的错误
  • 我知道,但似乎 django 正在后台某处复制数据。很不幸,您是用setUp 替换所有setUpTestData 还是仅替换那个?现在这纯粹是猜测,我会将问题添加为书签,以防有人提出解决方案
  • 什么让你觉得 Django 正在复制数据?
【解决方案2】:

设法找到解决方案。 StackOverflow 不允许我发布重复的答案,但我将答案放在了这篇文章中。

Django - Serializer throwing "Invalid pk - object does not exist" when setting ManyToMany attribute where foreign keyed object does exist

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-01
    • 2017-05-19
    • 1970-01-01
    • 2019-07-26
    • 1970-01-01
    相关资源
    最近更新 更多