Как с помощью Django Factory Boy создать фабрику для внешнего ключа? - PullRequest
0 голосов
/ 17 июня 2020

Я использую Django 3 с Factory Boy 2.12.0. Имею следующие модели. Обратите внимание, что второе зависит от первого ...

class ContactMethod(models.Model):
    class ContactTypes(models.TextChoices):
        EMAIL = 'EMAIL', _('Email')
        PHONE = 'PHONE', _('Phone')

    type = models.CharField(
        null=False,
        max_length=5,
        choices=ContactTypes.choices,
    )
    phone = PhoneNumberField(null=True)
    email = models.EmailField(null=True)

    class Meta:
        unique_together = ('phone', 'email',)
class Coop(models.Model):
    objects = CoopManager()
    name = models.CharField(max_length=250, null=False)
    types = models.ManyToManyField(CoopType)
    addresses = models.ManyToManyField(Address)
    enabled = models.BooleanField(default=True, null=False)
    phone = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_phone')
    email = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_email')
    web_site = models.TextField()

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

class PhoneContactMethodFactory(factory.DjangoModelFactory):
    """
        Define Contact Method Factory for a phone number
    """
    class Meta:
        model = ContactMethod

    type = ContactMethod.ContactTypes.EMAIL
    phone = "8005551234"


class EmailContactMethodFactory(factory.DjangoModelFactory):
    """
        Define Contact Method Factory for emails
    """
    class Meta:
        model = ContactMethod

    type = ContactMethod.ContactTypes.EMAIL
    email = "test@example.com"


class CoopFactory(factory.DjangoModelFactory):
    """
        Define Coop Factory
    """
    class Meta:
        model = Coop

    name = "test model"
    enabled = True
    phone = PhoneContactMethodFactory()
    email = EmailContactMethodFactory()
    web_site = "http://www.hello.com"

    @factory.post_generation
    def addresses(self, create, extracted, **kwargs):
        if not create:
            # Simple build, do nothing.
            return

        if extracted:
            # A list of types were passed in, use them
            for address in extracted:
                self.addresses.add(address)
        else:
            address = AddressFactory()
            self.addresses.add( address )

К сожалению, у меня, похоже, возникли проблемы с запуском этой фабрики, потому что мой метод тестирования

@pytest.mark.django_db
def test_coop_create(self):
    """ Test customer model """    # create customer model instance
    coop_from_factory = CoopFactory()
    self.assertIsNotNone(coop_from_factory)

    coop = Coop.objects.create(name='test')
    coop.addresses.set(coop_from_factory.addresses.all())
    self.assertIsNotNone(coop)

умирает на

coop_from_factory = CoopFactory()

с ошибка ниже. Как мне автоматически создать фабрику для внешнего ключа?

========= start ===========

FsetUp: Run once for every test method to setup clean data.
{}
.setUp: Run once for every test method to setup clean data.
.
======================================================================
ERROR: test_coop_create (tests.test_models.ModelTests)
Test customer model
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/django/db/backends/mysql/base.py", line 74, in execute
    return self.cursor.execute(query, args)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/cursors.py", line 170, in execute
    result = self._query(query)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/cursors.py", line 328, in _query
    conn.query(q)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/connections.py", line 517, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/connections.py", line 732, in _read_query_result
    result.read()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/connections.py", line 1075, in read
    first_packet = self.connection._read_packet()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/connections.py", line 684, in _read_packet
    packet.check_error()
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/protocol.py", line 220, in check_error
    err.raise_mysql_exception(self._data)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.7/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.IntegrityError: (1452, 'Cannot add or update a child row: a foreign key constraint fails (`test_directory_data`.`directory_coop`, CONSTRAINT `directory_coop_phone_id_4c7e2178_fk_directory_contactmethod_id` FOREIGN KEY (`phone_id`) REFERENCES `directory_contactmethod` (`id`))')

1 Ответ

0 голосов
/ 17 июня 2020

Вам нужно использовать factory.SubFactory для ForeignKey: https://factoryboy.readthedocs.io/en/latest/recipes.html#dependent -objects-foreignkey

Таким образом, это будет:

class CoopFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Coop

    phone = factory.SubFactory(PhoneContactFactory)
    email = factory.SubFactory(EmailContactFactory)

Ваш текущий код не делает того, что вы ожидаете: он создает контакт по телефону и контакт электронной почты при импорте модуля и повторно использует их в каждом экземпляре, созданном фабрикой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...