Django / Pytest / Splinter : IntegrityError дублирование ключа только в тесте
Я знаю, что это очень распространенная проблема, и я читал много подобных вопросов. Но я не могу найти никакого решения, и вот я здесь, с 987-м вопросом на Stackoverflow об ошибке Django Integrity.
Я начинаю проект Django с базой данных Postgres, чтобы познакомиться с фреймворком. Я сделал классическое создание профиля для пользователей, автоматизированное с помощью сигнала post_save. Вот модель:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.TextField(max_length=280, blank=True)
contacts = models.ManyToManyField(
"self",
symmetrical=True,
blank=True
)
А это сигнал, который идет вместе с ним :
def create_profile(sender, instance, created, **kwargs):
if created:
user_profile = Profile(user=instance)
user_profile.save()
post_save.connect(create_profile, sender=User, dispatch_uid="profile_creation")
Проект только начинается, и пока я создаю пользователей только в админке. С помощью сигнала post_save предполагается создать профиль с той же формой.
Вот настройка администратора :
class ProfileInline(admin.StackedInline):
model = Profile
class UserAdmin(admin.ModelAdmin):
model = User
list_display = ["username", "is_superuser"]
fields = ["username", "is_superuser"]
inlines = [ProfileInline]
Я использую pytest и Splinter для тестирования, и вот интеграционный тест, который не работает :
@pytest.mark.django_db
class TestAdminPage:
def test_profile_creation_from_admin(self, browser, admin_user):
browser.visit('/admin/login/')
username_field = browser.find_by_css('form input[name="username"]')
password_field = browser.find_by_css('form input[name="password"]')
username_field.fill(admin_user.username)
password_field.fill('password')
submit = browser.find_by_css('form input[type="submit"]')
submit.click()
browser.links.find_by_text("Users").click()
browser.links.find_by_partial_href("/user/add/").click()
browser.find_by_css('form input[name="username"]').fill('Super_pseudo')
browser.find_by_css('textarea[name="profile-0-description"]').fill('Super description')
browser.find_by_css('input[name="_save"]').click()
assert browser.url is '/admin/auth/user/'
assert Profile.objects.last().description is 'Super description'
Когда я запускаю это, я получаю эту ошибку :
django.db.utils.IntegrityError: duplicate key value violates unique constraint "profiles_profile_user_id_key"
DETAIL: Key (user_id)=(2) already exists.
Сначала я также увидел эту ошибку при создании пользователя на локальном сервере. Но только если я писал описание. Если я оставлял поле описания пустым, все работало нормально. Поэтому я написал этот интеграционный тест, чтобы решить проблему. Потом я много читал, кое-что подправил, и ошибка перестала возникать в моем локальном браузере. Но не в моем тестовом наборе.
Поэтому я использовал точку останова, там :
def create_profile(sender, instance, created, **kwargs):
breakpoint()
if created:
user_profile = Profile(user=instance)
user_profile.save()
И вот тут начинается самое интересное. Этот единственный тест вызывает сигнал 3 раза.
В первый раз он вызывается используемым мной приспособлением admin_user
.
(Pdb) from profiles.models import Profile
(Pdb) instance
<User: admin>
(Pdb) created
True
(Pdb) instance.profile
*** django.contrib.auth.models.User.profile.RelatedObjectDoesNotExist: User has no profile.
(Pdb) Profile.objects.count()
0
(Pdb) continue
Кажется законным, у пользователя admin нет профиля, почему бы и нет. Затем сигнал вызывается снова на том же экземпляре.
(Pdb) instance
<User: admin>
(Pdb) instance.profile
<Profile: admin>
(Pdb) Profile.objects.count()
1
(Pdb) Profile.objects.last()
<Profile: admin>
(Pdb) created
False
(Pdb) continue
Все еще законно, странно, но это ничего не делает. created
- False, поэтому он не создает второй профиль. Первый не нужен, но это не делает тест неудачным. И тут :
(Pdb) instance
<User: Super_pseudo>
(Pdb) created
True
(Pdb) instance.profile
<Profile: Super_pseudo>
(Pdb) Profile.objects.count()
1
(Pdb) Profile.objects.last()
<Profile: admin>
Это так странно. Профиль не сохраняется, но все равно выдает ошибку целостности. Он выглядит инстанцированным, когда я вызываю instance.profile
, я получаю что-то, но не похоже, что он сохранен в базе данных. Но ошибка все равно происходит. Я понятия не имею, я потратил уже несколько часов, и я не знаю, что искать.
Чувствую, что упускаю что-то важное, и поэтому прошу вашей помощи.
Редактирование
Я пробовал обновить сигнал с помощью if created and not kwargs.get('raw', False):
, но это не работает.