Python 3.x 在FactoryBoy中,如何使用空的多对多成员字段设置工厂?

Python 3.x 在FactoryBoy中,如何使用空的多对多成员字段设置工厂?,python-3.x,django,unit-testing,django-3.0,factory-boy,Python 3.x,Django,Unit Testing,Django 3.0,Factory Boy,我将Django3与Python3.8一起使用。我有以下型号 class Coop(models.Model): objects = CoopManager() name = models.CharField(max_length=250, null=False) types = models.ManyToManyField(CoopType, blank=False) addresses = models.ManyToManyField(Address)

我将Django3与Python3.8一起使用。我有以下型号

class Coop(models.Model):
    objects = CoopManager()
    name = models.CharField(max_length=250, null=False)
    types = models.ManyToManyField(CoopType, blank=False)
    addresses = models.ManyToManyField(Address)
    enabled = models.BooleanField(default=True, null=False)
    phone = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_phone')
    email = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_email')
    web_site = models.TextField()
我已经创建了以下工厂(使用factory boy)来尝试在测试中创建模型

class CoopFactory(factory.DjangoModelFactory):
    """
        Define Coop Factory
    """
    class Meta:
        model = Coop

    name = "test model"
    enabled = True
    phone = factory.SubFactory(PhoneContactMethodFactory)
    email = factory.SubFactory(EmailContactMethodFactory)
    web_site = "http://www.hello.com"

    @factory.post_generation
    def addresses(self, create, extracted, **kwargs):
        if not create:
            # Simple build, do nothing.
            return

        if extracted:
            # A list of types were passed in, use them
            for address in extracted:
                self.addresses.add(address)
        else:
            address = AddressFactory()
            self.addresses.add( address )

    @factory.post_generation
    def types(self, create, extracted, **kwargs):
        if not create:
            # Simple build, do nothing.
            return

        if extracted:
            # A list of types were passed in, use them
            for type in extracted:
                self.types.add(type)
        else:
            print("Creating type ...\n")
            type = CoopTypeFactory()
            self.types.add( type )
但是我很难创建一个多对多字段(类型)为空的工厂。我试过下面的方法

@pytest.mark.django_db
def test_coop_create_with_no_types(self):
    """ Test customer model """    # create customer model instance
    coop = CoopFactory.create(types=[])
    print("size: ", coop.types.all().count())
    self.assertIsNotNone(coop)
    self.assertIsNotNone( coop.id )
但是
types.all().count()
的值始终等于1。如何正确设置多对多字段为空的工厂

编辑:根据给出的答案,在工厂使用的成员字段中传递的正确方式是什么?我试过了

@pytest.mark.django_db
def test_coop_create_with_existing_type(self):
    """ Test customer model """    # create customer model instance
    coop_from_factory = CoopFactory()
    self.assertIsNotNone(coop_from_factory)

    coop_types = coop_from_factory.types
    coop = CoopFactory.create(types=[coop_types.all().first()], addresses=coop_from_factory.addresses.all())
    self.assertIsNotNone(coop)
但是得到了这个错误,对于“for uu in range(extracted):”行

跳过

        else:
            print("Creating type ...\n")
            type = CoopTypeFactory()
            self.types.add( type )
它始终将创建CoopType作为默认值

文档中可能不清楚的一点是,钩子总是被调用。这意味着示例代码中所有生成后钩子中的else语句都将被调用

更多信息:

如果我想直接创建默认值,我经常使用的模式是 向工厂添加一个函数,在本例中,该函数将转换为:

@factory.post_generation
def create_types(self, create, extracted, **kwargs):
    if not create:
        # Simple build, do nothing.
        return

    if extracted:
        for _ in range(extracted):
            self.types.add(CoopTypeFactory())
启用使用
CoopFactory(创建类型=3)

以下是我的完整示例:

@factory.post_generation
def types(self, create, extracted, **kwargs):
    if not create:
        # Simple build, do nothing.
        return

    if extracted:
        # A list of types were passed in, use them
        for type in extracted:
            self.types.add(type)
    # Removed this because it always creates 1 CoopType as default and
    # it may not be the desired behaviour for all tests.
    # else:
    #     print("Creating type ...\n")
    #     type = CoopTypeFactory()
    #     self.types.add( type )

# Adding this function to have a simple way of just adding default CoopTypes
@factory.post_generation
def create_types(self, create, extracted, **kwargs):
    if not create:
        # Simple build, do nothing.
        return

    if extracted: # This must be an integer
        for _ in range(extracted):
            self.types.add(CoopTypeFactory())
这提供了可选的用法:

CoopFactory(create_types=3)
将调用create_types并将int 3放入param extracted并创建3个默认的CoopTypes。(这使使用变得简单)


CoopFactory(types=[CoopTypeFactory()])
将调用这些类型,并在提取的参数中放置一个包含1个CoopType的列表。(如果这些对象需要某些特定值,则可以对如何创建合作类型进行更多控制)

修复方法是将
如果提取
更改为
如果提取不是无

解释 在Python中,空列表为false 1,但不是
None

coop=CoopFactory.create(类型=[])
空列表
[]
作为参数
提取
传递给生成后钩子
类型

@factory.post\u生成
def类型(自我、创建、提取、**kwargs):
如果没有,请创建:
#简单的构建,什么都不做。
返回
如果提取:
#已传入类型列表,请使用它们
对于提取的类型:
self.types.add(类型)
其他:
打印(“创建类型…\n”)
type=CoopTypeFactory()
self.types.add(类型)
由于
if extracted
是真值test1,因此假空列表落在
else
块中,其中创建了
类型
。因此,
types.all().count()
的值等于1


1

嗨,谢谢你。当使用“for u in range(extracted):”时,为我的成员字段传入非空参数的正确方法是什么?我已经根据你的建议,在我的问题中添加了一个编辑错误。看起来你没有在错误日志中传递一个整数。我加了一个更详细的解释。我希望它澄清。哦,我看到你在传递一个数字,并告诉它创建X个类型的数字。我实际上是在寻找一种方法来传递工厂将用于类型的对象集合。换句话说,我想说,创建这个对象,使用所有默认字段,但是如果我指定“类型”,那么使用我指定的。
@factory.post_generation
def types(self, create, extracted, **kwargs):
    if not create:
        # Simple build, do nothing.
        return

    if extracted:
        # A list of types were passed in, use them
        for type in extracted:
            self.types.add(type)
    # Removed this because it always creates 1 CoopType as default and
    # it may not be the desired behaviour for all tests.
    # else:
    #     print("Creating type ...\n")
    #     type = CoopTypeFactory()
    #     self.types.add( type )

# Adding this function to have a simple way of just adding default CoopTypes
@factory.post_generation
def create_types(self, create, extracted, **kwargs):
    if not create:
        # Simple build, do nothing.
        return

    if extracted: # This must be an integer
        for _ in range(extracted):
            self.types.add(CoopTypeFactory())