使用 Django Factory Boy,如何为外键生成工厂?
With Django Factory Boy, how do I generate a factory for a foreign key?
我将 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`))')
您需要使用 factory.SubFactory
作为外键: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)
您当前的代码没有达到您的预期:它创建了一个 phone 联系人和一个电子邮件联系人 当模块被导入时 ,并重新使用它们在工厂生成的每个实例上。
我将 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`))')
您需要使用 factory.SubFactory
作为外键: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)
您当前的代码没有达到您的预期:它创建了一个 phone 联系人和一个电子邮件联系人 当模块被导入时 ,并重新使用它们在工厂生成的每个实例上。