Как устранить CyclicDefinitionError в вызове factory_boy SubFactory?

у меня есть следующие модели

# in ModelA_App/models.py
class ModelA(models.Model):
    TYPEA = 1
    TYPEB = 2
    TYPE_CHOICES = (
       (TYPEA, 'TypeA'),
       (TYPEB, 'TypeB')
    )
    type = models.PositiveSmallIntegerField(choices=TYPE_CHOICES)
    name - models.CharField(max_length = 100)

#in ModelB_App/models.py
from ModelA_App.models import ModelA

class ModelB(models.Model):
    label = models.TextFiled()
    model_a = models.OneToOneField(ModelA, on_delete=models.CASCADE)

И у меня есть следующие заводы:

#in ModelA_App/factories.py
class ModelAFactory(factory.django.DjangoModelFactory):
    class Meta:
       model = ModelA

    name = factory.Faker('word')
    type = ModelA.TYPEA

#in ModelB_App/factories.py
from ModelA_App.models import ModelA
from ModelA_App.factories import ModelAFactory

class ModelBFactory(factory.django.DjangoModelFactory):
    class Meta:
       model = ModelB

    label = factory.Faker('text')
    model_a = SubFactory(ModelAFactory, type = factory.LazyAttribute(lambda o: '%d' % o.type))

    class Params:
       type = ModelA.TYPEA

Я хотел бы иметь возможность создать объект ModelB с ModelA, имеющим TYPEB. Попытка строки ModelBFactory.create(type = ModelA.TYPEB) приводит к ошибке:

factory.errors.CyclicDefinitionError: циклическое ленивое определение атрибута для 'type'; цикл найден в ['type']

Кроме того, когда я меняю имя type в классе Params, например, на model_type имеющий:

model_a = SubFactory(ModelAFactory, type = factory.LazyAttribute(lambda o: '%d' % o.model_type))

Это терпит неудачу с

AttributeError: параметр model_type неизвестен.

Как я могу достичь своей цели?


person sebap123    schedule 21.07.2020    source источник


Ответы (2)


Когда вы пишете SubFactory, дополнительные определения, которые вы предоставляете, привязываются к этой подфабрике. То, что вы написали, эквивалентно:

class SubModelAFactory(ModelAFactory):
    class Meta:
        # Not required - implied by class inheritance
        model = models.ModelA

    type = factory.LazyAttribute(lambda o: '%d' % o.type)

class ModelBFactory(factory.django.DjangoModelFactory):
    ...
    model_a = factory.SubFactory(SubModelFactory)

Вместо этого вам следует подняться на уровень выше, как описано в в SelfAttribute документах:

class ModelBFactory(factory.django.DjangoModelFactory):
    ...
    model_a = factory.SubFactory(
        SubModelFactory,
        type=factory.LazyAttribute(lambda o: '%d' % o.factory_parent.type),

        # If the value can be passed without conversion, use:
        type=factory.SelfAttribute('..type'),
    )
person Xelnor    schedule 21.07.2020

Я нашел решение. ModelBFactory должно выглядеть так:

#in ModelB_App/factories.py
from ModelA_App.models import ModelA
from ModelA_App.factories import ModelAFactory

class ModelBFactory(factory.django.DjangoModelFactory):
    class Meta:
       model = ModelB

    label = factory.Faker('text')
    model_a = factory.LazyAttribute(lambda o: ModelAFactory(type = o.type))

    class Params:
       type = ModelA.TYPEA

Вместо того, чтобы поместить атрибут ModelAFactory как LazyAttribute, мне пришлось указать всю фабрику как один.

person sebap123    schedule 21.07.2020
comment
это решение работает, но вы потеряете все преимущества SubFactory: совместное использование стратегии (ModelBFactory.build / ModelBFactory.create), возможность переопределения полей во время вызова (ModelBFactory(model_a__name='example')) и т. д. - person Xelnor; 21.07.2020