Can't pass size to RelatedFactoryList from parent factory's Params
Description
I can't seem to find a way to pass the size kwarg from a parent factory's params through to a RelatedFactoryList. I've tried every permutation of LazyAttribute to SelfAttribute to lambdas.
Model / Factory code
class BarFactory(factory.DjangoModelFactory)
class Meta:
model = Bar
class FooFactory(factory.DjangoModelFactory)
class Meta:
model = Foo
class Params:
size = 2
bars = factory.RelatedFactoryList(BarFactory, size=factory.SelfAttribute('..size'))
The issue
FooFactory()
# ->
File "/usr/local/lib/python3.7/site-packages/factory/base.py", line 46, in __call__
return cls.create(**kwargs)
File "/usr/local/lib/python3.7/site-packages/factory/base.py", line 564, in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
File "/usr/local/lib/python3.7/site-packages/factory/django.py", line 141, in _generate
return super(DjangoModelFactory, cls)._generate(strategy, params)
File "/usr/local/lib/python3.7/site-packages/factory/base.py", line 501, in _generate
return step.build()
File "/usr/local/lib/python3.7/site-packages/factory/builder.py", line 272, in build
step.resolve(pre)
File "/usr/local/lib/python3.7/site-packages/factory/builder.py", line 221, in resolve
self.attributes[field_name] = getattr(self.stub, field_name)
File "/usr/local/lib/python3.7/site-packages/factory/builder.py", line 375, in __getattr__
extra=context,
File "/usr/local/lib/python3.7/site-packages/factory/declarations.py", line 321, in evaluate
return self.generate(step, defaults)
File "/usr/local/lib/python3.7/site-packages/factory/declarations.py", line 411, in generate
return step.recurse(subfactory, params, force_sequence=force_sequence)
File "/usr/local/lib/python3.7/site-packages/factory/builder.py", line 233, in recurse
return builder.build(parent_step=self, force_sequence=force_sequence)
File "/usr/local/lib/python3.7/site-packages/factory/builder.py", line 299, in build
context=postgen_context,
File "/usr/local/lib/python3.7/site-packages/factory/declarations.py", line 694, in call
else self.size())]
TypeError: 'SelfAttribute' object is not callable
Notes
If there is a different strategy for passing the size of a RelatedFactoryList from it's parent Factory (i.e. being able to specify size of the list when instantiating the parent factory via a kwarg) I'd be happy to know of an alternative approach.
I'm assuming this is due to bars being a ManyToManyField... it seems like in this case I may not be able to use a RelatedFactoryList?
I also ran into this issue with RelatedFactoryList.
I got around this by using the post_generation hook to create my related factory list manually. From what I can tell, there is no support for the size param to be a LazyAttribute or SelfAttribute. There's an inactive but relevant PR about this at #727.
@timorthi indeed; this could be adressed by #774 — which is significant change, for which I'd like to gather some feedback before going forward ;)
I had the same issue. Here was my quick solution (I know this would cause issues in some cases, but it worked for my usage)
class RelatedFactoryVariableList(RelatedFactoryList):
"""allows overriding ``size`` during factory usage, e.g. ParentFactory(list_factory__size=4)"""
def call(self, instance, step, context):
size = context.extra.get('size', self.size)
assert isinstance(size, int)
return [super(RelatedFactoryList, self).call(instance, step, context) for i in range(size)]
So for OP's example, you would use it like FooFactory(bars__size=4)
I had the same issue. Here was my quick solution (I know this would cause issues in some cases, but it worked for my usage)
Nice workaround!
In my case, I needed to remove size from context.extra (by using pop instead of get), otherwise the call to the super constructor would throw TypeError: 'size' is an invalid keyword argument for Bar. So what works for me is:
class RelatedFactoryVariableList(RelatedFactoryList):
"""allows overriding ``size`` during factory usage, e.g. ParentFactory(list_factory__size=4)"""
def call(self, instance, step, context):
size = context.extra.pop('size', self.size)
assert isinstance(size, int)
return [super(RelatedFactoryList, self).call(instance, step, context) for i in range(size)]