工厂男孩产生“超出最大递归深度”错误

时间:2018-03-16 20:57:44

标签: python django factory-boy

我想定义两个模型CompanyPackage。每个Package只有一个Company,但Company可以有多个Packages。但是,每个公司只能有一个default_package(可以为null)。我把它设置如下:

class Company(models.Model):
    default_package = models.OneToOneField(
        'dashboard.Package',
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name='default_for_%(class)s')


class Package(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)

其中dashboard是应用标签。

为了简化这些模型的测试,我使用factory_boy为他们创建了测试工厂,如下所示:

import factory
from .models import Company, Package

class CompanyFactory(factory.Factory):
    class Meta:
        model = Company 

    default_package = factory.SubFactory('dashboard.test_factories.PackageFactory')


class PackageFactory(factory.Factory):
    class Meta:
        model = Package

    company = factory.SubFactory(CompanyFactory)

现在我正在尝试两项测试:

class DefaultPackageTest(TestCase):
    def test_1(self):
        company = Company.objects.create()

    def test_2(self):
        company = CompanyFactory()

第一个只创建一个Company,而第二个尝试使用CompanyFactory执行相同的操作。

然而,奇怪的是,第一次测试通过了,而第二次测试失败了:

  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/factory/builder.py", line 233, in recurse
    return builder.build(parent_step=self, force_sequence=force_sequence)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/factory/builder.py", line 272, in build
    step.resolve(pre)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/factory/builder.py", line 221, in resolve
    self.attributes[field_name] = getattr(self.stub, field_name)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/factory/builder.py", line 355, in __getattr__
    declaration = self.__declarations[name]
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/factory/builder.py", line 121, in __getitem__
    context=self.contexts[key],
RecursionError: maximum recursion depth exceeded

----------------------------------------------------------------------
Ran 2 tests in 0.012s

FAILED (errors=1)
Destroying test database for alias 'default'...

知道为什么这不起作用?我相信我已经通过提供CompanyFactory子工厂的完整路径来跟踪文档(http://factoryboy.readthedocs.io/en/latest/reference.html#circular-imports)。

更新

这个用例似乎是由Factory Boy的post-generation hooks解决的。看起来很有希望的是RelatedFactory,其中给出了以下示例:

class CityFactory(factory.Factory):
    class Meta:
        model = City

    capital_of = None
    name = "Toronto"

class CountryFactory(factory.Factory):
    class Meta:
        model = Country

    lang = 'fr'
    capital_city = factory.RelatedFactory(CityFactory, 'capital_of', name="Paris")

在Python REPL中测试如下:

>>> france = CountryFactory()
>>> City.objects.get(capital_of=france)
<City: Paris>
但是,我很难将这个例子应用到我的情况中。 (文档中没有CityCountry模型的文本说明或代码,这没有任何帮助。在我的情况下,似乎capital_city类似于default_package,所以我尝试将其变成RelatedFactory,就像这样,

default_package = factory.RelatedFactory('dashboard.test_factories.PackageFactory')

但我仍然得到同样的错误。

1 个答案:

答案 0 :(得分:0)

因此,我通过实现post_generation函数实现了这一点:

    class PackageFactory(factory.Factory):
        class Meta:
            model = Package

        company = factory.SubFactory('dashboard.test_factories.CompanyFactory')

    class CompanyFactory(factory.Factory):
        class Meta:
            model = Company 

        @factory.post_generation
        def default_package(self, create, _, **__):
            PackageFactory(company=self)

company=self kwarg停止了递归,并使用所需的默认包属性正确创建了工厂。