To be able to specify the number of related objects in parent factory:
models.py
class Company(models.Model):
name = models.CharField(max_length=255)
class ContactPerson(models.Model):
name = models.CharField(max_length=255)
company = models.ForeignKey(Company, on_delete=CASCADE, related_name='contacts')
factories.py
class CompanyFactory(factory.django.DjangoModelFactory):
name = factory.Faker('company')
class Meta:
model = Company
@factory.post_generation
def add_contacts(self, create, how_many, **kwargs):
# this method will be called twice, first time how_many will take the value passed
# in factory call (e.g., add_contacts=3), second time it will be None
# (see factory.declarations.PostGeneration#call to understand how how_many is populated)
# ContactPersonFactory is therefore called +1 times but somehow we get right amount of objs
at_least = 1
if not create:
return
for n in range(how_many or at_least):
ContactPersonFactory(contact=self)
class ContactPersonFactory(factory.django.DjangoModelFactory):
name = factory.Faker('first_name')
class Meta:
model = ContactPerson
tests.py
company = CompanyFactory(company_name='ACME ltd', add_contacts=4)
print(repr(company.name), len(company.contacts.all()))
company = CompanyFactory(company_name='ACME ltd')
print(repr(company.name), len(company.contacts.all()))
---
'ACME ltd' 4
'ACME ltd' 1
If you are ok with always just one child, the docs solution works well:
models.py
class CompanyFactory(factory.django.DjangoModelFactory):
name = factory.Faker('company')
whatever_really = factory.RelatedFactory('my_app.factories.ContactPersonFactory', 'contact')
class Meta:
model = Company
note the full path to the related factory.
tests.py
company = CompanyFactory(company_name='ACME ltd')
print(repr(company.name), len(company.contacts.all()))
---
'ACME ltd' 1
versions used
$ pip freeze | egrep 'factory|Faker|Django'
Django==2.0.4
factory-boy==2.10.0
Faker==0.8.13
$ python -V
Python 3.6.5