主要解决方案
为此需要做两件事:
parameters
和LazyAttribute
(链接指向他们的文档,了解更多详情)。
参数就像工厂属性,不会传递给将要创建的实例。
在这种情况下,它们提供了一种对 Bar
列表的长度进行参数化的方法。
但是为了使用参数自定义工厂中的字段,我们需要访问self
,
也就是说,正在构建的实例。
我们可以使用 LazyAttribute
来实现这一点,它是一个声明,它接受一个带有一个参数的函数:
正在构建的对象。
正是我们所需要的。
所以问题中的片段可以重写如下:
class FooFactory(factory.Factory):
class Meta:
model = command.Foo
class Params:
number_of_bars = 1
foo_uuid = factory.Faker("uuid4")
bars = factory.LazyAttribute(lambda self: [BarFactory()] * self.number_of_bars)
并像这样使用:
foo = FooFactory(number_of_bars=3)
如果未提供 number_of_bars
参数,则使用默认值 1
。
缺点
遗憾的是,我们在这里可以做的事情有一些限制。
在另一个工厂的定义中使用工厂的首选方法是通过
SubFactory
。
这是首选的原因有两个:
- 它尊重用于父工厂的构建策略
- 它收集额外的关键字参数来自定义子工厂
第一个意思是如果我们使用 SubFactory
在 Bar
中构建一个 FooFactory
并用 FooFactory
或 FooFactory.create
调用 FooFactory.build
,
Bar
子工厂会尊重这一点并使用相同的策略。
综上所述,构建策略只构建一个实例,
而创建策略构建并将实例保存到正在使用的持久存储中,
例如一个数据库,所以尊重这个选择很重要。
请参阅docs
了解更多详情。
第二个意思是我们可以在调用Bar
的时候直接自定义FooFactory
的属性。
例如:
foo = FooFactory(bar__id=2)
会将 id
的 bar
的 foo
设置为 2
,而不是 Bar
子工厂默认生成的内容。
但是我找不到通过 SubFactory
使用 Params
和 动态长度的方法。
据我所知,在 FactoryBoy 需要 SubFactory
的上下文中,无法访问参数的值。
问题是让我们访问正在构建的对象的声明总是期望返回一个最终的值,
不是以后要调用的另一个工厂。
这意味着,在上面的例子中,如果我们改写:
class FooFactory(factory.Factory):
# ... rest of the factory
bars = factory.LazyAttribute(lambda self: [factory.SubFactory(BarFactory)] * self.number_of_bars)
然后调用它就像
foo = FooFactory(number_of_bars=3)
将导致 foo
在 BarFactory
中有 3 个 foo.bars
的列表而不是 3 个 Bar
的列表。
并使用 SelfAttribute
,
这是一种引用正在构建的实例的另一个属性的方法,也不起作用
因为它不是在像这样的声明中的表达式的其余部分之前计算的:
class FooFactory(factory.Factory):
# ... rest of the factory
bars = factory.List([factory.SubFactory(BarFactory)] * SelfAttribute("number_of_bars"))
这会引发 TypeError: can't multiply sequence by non-int of type 'SelfAttribute'
。
一种可能的解决方法是预先调用 BarFactory
并将其传递给 FooFactory
:
number_of_bars = 3
bars = BarFactory.create_batch(number_of_bars)
foo = FooFactory(bars=bars)
但这肯定没有那么好。
我最近发现的另一个是 RelatedFactoryList
。
但这仍然是实验性的,它似乎没有办法访问参数。
此外,由于它是在基础工厂之后生成的,如果实例构造函数也可能不起作用
期望该属性作为参数。
本文链接:https://www.f2er.com/3123964.html