Reputation: 427
I'm using Factory boy for my models. Another model has a ForeignKey to it, and the first obj of that model is used for string representation of the model. Example structure:
class A(models.Model):
...
def __str__(self):
return self.reverseForeignKey_set.first().name
class AFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.A
some_attribute = factory.RelatedFactory(
ReverseModelFactory,
'a',
)
But when I use factory_boy to create, it gives me this error:
Traceback (most recent call last):
File "/home/rahul/Documents/projects/healbot_current/healbot/functional_tests/tests.py", line 21, in setUp
factories.AFactory.create()
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/base.py", line 623, in create
return cls._generate(True, attrs)
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/base.py", line 554, in _generate
results[name] = decl.call(obj, create, extraction_context)
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/declarations.py", line 624, in call
return factory.simple_generate(create, **passed_kwargs)
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/base.py", line 709, in simple_generate
return cls.generate(strategy, **kwargs)
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/base.py", line 676, in generate
return action(**kwargs)
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/base.py", line 622, in create
attrs = cls.attributes(create=True, extra=kwargs)
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/base.py", line 449, in attributes
utils.log_repr(extra),
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/factory/utils.py", line 142, in log_repr
return compat.force_text(repr(obj))
File "/home/rahul/Envs/healbot/lib/python3.6/site-packages/django/db/models/base.py", line 588, in __repr__
u = six.text_type(self)
File "/home/rahul/Documents/projects/healbot_current/healbot/diagnosis/models.py", line 57, in __str__
return self.reverseForeignKey_set.first().name
AttributeError: 'NoneType' object has no attribute 'name'
Is there something I can do to get around this? Like override the str method on model A just for factory_boy?
Upvotes: 1
Views: 3230
Reputation: 331
You have such a problem because A
model instance is creating earlier than a ReverseModel
instance.
As a factory_boy documentation says:
RelatedFactory: this builds or creates a given factory after building/creating the first Factory.
See http://factoryboy.readthedocs.io/en/latest/reference.html#post-generation-hooks
First of all you should create your ReverseModel
and then your A
model instance.
As the factory_boy documentation says in recepies:
When one attribute is actually a complex field (e.g a ForeignKey to another Model), use the SubFactory declaration
So you can define a factory for your FK model and define it as a SubFactory in an AFactory.
# models.py
class A(models.Model):
...
def __str__(self):
return self.reverseForeignKey_set.first().name
# factories.py
import factory
from . import models
class ReverseModelFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.ReverseModel
# Define your model fields
attr = factory.Faker('text', max_nb_chars=255)
class AFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.A
some_attribute = factory.SubFactory(ReverseModelFactory)
In this case your ReverseModelFactory object will be created at first that should solve your problem. See http://factoryboy.readthedocs.io/en/latest/recipes.html for more information.
I should also note that if you want to handle ManyToMany field, you shall use the post_generation hook:
# models.py
class Group(models.Model):
name = models.CharField()
class User(models.Model):
name = models.CharField()
groups = models.ManyToManyField(Group)
# factories.py
class GroupFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Group
name = factory.Sequence(lambda n: "Group #%s" % n)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
name = "John Doe"
@factory.post_generation
def groups(self, create, extracted, **kwargs):
if not create:
# Simple build, do nothing.
return
if extracted:
# A list of groups were passed in, use them
for group in extracted:
self.groups.add(group)
See http://factoryboy.readthedocs.io/en/latest/recipes.html#simple-many-to-many-relationship for more information.
Upvotes: 1