django factory boy factory с отношениями OneToOne и смежными областями

Я использую Factory Boy для создания тестовых фабрик для моего приложения django. Модель, с которой у меня возникла проблема, представляет собой очень простую модель учетной записи, которая имеет отношение OneToOne к модели аутентификации пользователя django (с использованием django ‹ 1.5):

# models.py
from django.contrib.auth.models import User
from django.db import models

class Account(models.Model):
    user = models.OneToOneField(User)
    currency = models.CharField(max_length=3, default='USD')
    balance = models.CharField(max_length="5", default='0.00') 

Вот мои заводы:

# factories.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User

import factory

from models import Account


class AccountFactory(factory.django.DjangoModelFactory):
    FACTORY_FOR = Account

    user = factory.SubFactory('app.factories.UserFactory')
    currency             = 'USD'
    balance              = '50.00'

class UserFactory(factory.django.DjangoModelFactory):
    FACTORY_FOR = User

    username = 'bob'
    account = factory.RelatedFactory(AccountFactory)

Поэтому я ожидаю, что фабричный мальчик создаст связанную UserFactory всякий раз, когда вызывается AccountFactory:

# tests.py 
from django.test import TestCase

from factories import AccountFactory

class AccountTest(TestCase):

    def setUp(self):
        self.factory = AccountFactory()

    def test_factory_boy(self):
        print self.factory.id

Однако при запуске теста создается впечатление, что создается несколько пользовательских моделей, и я вижу ошибку интеграции:

IntegrityError: column username is not unique

В документации упоминается о наблюдении за циклами при работе с циклическим импортом, но я не уверен, происходит ли это, и как я могу это исправить. документы

Если кто-нибудь, знакомый с Factory Boy, может ответить или дать некоторое представление о том, что может быть причиной этой ошибки целостности, мы будем очень признательны!


person darko    schedule 09.07.2013    source источник
comment
К вашему сведению, ошибка возникает, даже если нет сигнала post_save.   -  person alecxe    schedule 15.07.2013
comment
Вы правы, я соответствующим образом отредактировал примеры кода.   -  person darko    schedule 15.07.2013


Ответы (2)


Я считаю, что это потому, что у вас есть циклическая ссылка в ваших заводских определениях. Попробуйте удалить строку account = factory.RelatedFactory(AccountFactory) из определения UserFactory. Если вы всегда будете вызывать создание учетной записи через AccountFactory, вам не нужна эта строка.

Кроме того, вы можете рассмотреть возможность присоединения последовательности к полю имени, чтобы, если вам когда-либо понадобится более одной учетной записи, они создавались автоматически.

Измените: username = "bob" на username = factory.Sequence(lambda n : "bob {}".format(n)), и ваши пользователи будут называться «bob 1», «bob 2» и т. д.

person hgcrpd    schedule 16.07.2013

Чтобы передать результат вызова UserFactory в AccountFactory, вы должны использовать factory_related_name (docs )

Код выше работает следующим образом:

Итак, вам нужно написать UserFactory так:

class UserFactory(factory.django.DjangoModelFactory):
    account = factory.RelatedFactory(AccountFactory, factory_related_name='user')
    username = factory.Sequence(lambda a: 'email%[email protected]' % a)
    # rest of code

Но вы все еще можете испытывать проблемы с уже написанными тестами. Представьте, что у вас есть в тестах такие места, как следующие:

user = UserFactory()
account = Account(user=user)

Тогда добавление RelatedFactory нарушит тесты. Если в вашем проекте не так много тестов и участников, вы можете их переписать. Но если нет, то это не вариант. Вот как с этим можно справиться:

class UserFactory(factory.django.DjangoModelFactory):
    class Params:
        generate_account = factory.Trait(
            account=factory.RelatedFactory(AccountFactory, factory_related_name='user')
        )

Тогда приведенный выше код не будет нарушен, потому что вызов по умолчанию UserFactory не будет создавать экземпляр AccountFactory. Чтобы создать пользователя с учетной записью:

user_with_account = UserFactory(generate_account=True)
person davemus    schedule 13.05.2021