Python:Factory Boy生成对象创建时指定的长度列表

我正在尝试使用Factoryboy在创建时指定长度的对象中创建列表。

我可以创建列表,但是由于提供的长度/大小的惰性,每次尝试创建具有指定长度的列表都会导致问题。

这是我到目前为止所拥有的:

class FooFactory(factory.Factory):

    class Meta:
        model = command.Foo

    foo_uuid = factory.Faker("uuid4")
    bars = factory.List([
        factory.LazyAttribute(lambda o: BarFactory()
        for _ in range(3))
    ])

这将创建3个随机条的列表。我尝试使用Params和exclude的组合,但是由于range期望一个Int,并且直到以后才会延迟加载int,因此会导致错误。

我想要一个类似于后代生成一对多关系的方法。

foo = FooFactory(number_of_bars=5)

有人有运气吗?

chenyong715 回答:Python:Factory Boy生成对象创建时指定的长度列表

主要解决方案

为此需要做两件事: parametersLazyAttribute (链接指向他们的文档,了解更多详情)。

参数就像工厂属性,不会传递给将要创建的实例。 在这种情况下,它们提供了一种对 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。 这是首选的原因有两个:

  1. 它尊重用于父工厂的构建策略
  2. 它收集额外的关键字参数来自定义子工厂

第一个意思是如果我们使用 SubFactoryBar 中构建一个 FooFactory 并用 FooFactoryFooFactory.create 调用 FooFactory.buildBar 子工厂会尊重这一点并使用相同的策略。 综上所述,构建策略只构建一个实例, 而创建策略构建将实例保存到正在使用的持久存储中, 例如一个数据库,所以尊重这个选择很重要。 请参阅docs 了解更多详情。

第二个意思是我们可以在调用Bar的时候直接自定义FooFactory的属性。 例如:

foo = FooFactory(bar__id=2)

会将 idbarfoo 设置为 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)

将导致 fooBarFactory 中有 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

大家都在问