Django - "psycopg2.errors.ForeignKeyViolation: insert or update on table" при выполнении "python manage.py test", но не при выполнении одиночного теста

Итак, я сделал еще одно сообщение относительно этой проблемы и решил разложить ее еще подробнее, чтобы посмотреть, смогут ли добрые люди из 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.py. PostFactory - генератор фиктивных данных.

@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".

Поскольку тест проходит на test cheers.test.ExploreTest, но не проходит на test, то определенно какой-то тест из другого пакета портит данные.

Не уверен, что это поможет решить проблему, но поскольку вы используете Django 3.2, в docs:

есть небольшое примечание.

Изменения в Django 3.2:

Объекты, назначенные атрибутам класса в setUpTestData() должны поддерживать создание глубоких копий с помощью copy.deepcopy() для того, чтобы изолировать их от изменений, выполняемых каждым из методов тестирования. В предыдущих версиях Django эти объекты использовались повторно, и изменения, внесенные в них, сохранялись между тестовыми методами. сохранялись между тестовыми методами.

Копирование экземпляра модели с помощью copy.deepcopy() не работает как ожидалось, требуется дополнительный шаг для создания нового экземпляра. Я не могу найти это нигде в документации, но я использовал код из этого ответа несколько раз (и он работает на Django 3.2 с Postgres):

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

Если это так, я бы просто выбрал setUp вместо setUpTestData, потому что он воссоздает тестовую базу данных перед каждым тестом.
Это медленнее, и вам, возможно, придется изменить определенные данные для некоторых тестов, но это отлично работает и проще в обслуживании.

Удалось найти решение. StackOverflow не позволяет мне публиковать дубликаты ответов, но я поместил ответ в этот пост.

Django - Serializer бросает "Invalid pk - object does not exist" при установке атрибута ManyToMany, когда объект с внешним ключом существует

Вернуться на верх