【问题标题】:Factory Boy with Django ManyToMany models带有 Django ManyToMany 模型的 Factory Boy
【发布时间】:2026-01-22 12:30:01
【问题描述】:

我在同一个模块上有 3 个模型,app.models.py,如下所示。其他一些模型可能会出现在代码中,但并不相关。

选项

class Optional(models.Model):
    name = models.CharField(_('Nome'), max_length=255)
    type = models.CharField(_('Tipo'), max_length=50, null=True, blank=True)
    description = models.TextField(_('Descrição'), null=True, blank=True)
    provider = models.ForeignKey('providers.Provider', null=True, blank=True)
    charge = models.ForeignKey('Charge', null=True, blank=True)

    def __str__(self):
        return self.name

覆盖范围

class Coverage(models.Model):
    code = models.CharField(_('Código'), max_length=20, null=True, blank=True)
    vehicle_code = models.CharField(_('Código do veículo (ACRISS)'), max_length=4, null=True, blank=True)
    charge = models.ForeignKey('Charge', null=True, blank=True)

车辆

class Vehicle(models.Model):

    code = models.CharField(_('Código'), max_length=100)
    description = models.CharField(_('Descrição'), max_length=255, null=True, blank=True)
    model = models.CharField(_('Modelo'), max_length=100, null=True, blank=True)
    brand = models.CharField(_('Fabricante'), max_length=100, null=True, blank=True)
    group = models.ForeignKey('Group', null=True, blank=True)
    optionals = models.ManyToManyField('Optional', related_name='vehicle_optional')
    coverages = models.ManyToManyField('Coverage', related_name='vehicle_coverage')

    def __str__(self):
        return self.code

我正在尝试使用 factory_boy 从该模型创建固定装置。

class CoverageFactory(factory.Factory):
    class Meta:
        model = Coverage
    charge = factory.SubFactory(ChargeFactory)

class OptionalFactory(factory.Factory):
    class Meta:
        model = Optional
    provider = factory.SubFactory(ProviderFactory)
    charge = factory.SubFactory(ChargeFactory)

class VehicleFactory(factory.Factory):
    class Meta:
        model = Vehicle
    group = factory.SubFactory(GroupFactory)
    optionals = factory.SubFactory(OptionalFactory)
    coverages = factory.SubFactory(CoverageFactory)

在我的测试中,它是这样实例化的:

optional = OptionalFactory(
    name="GPS",
    type="13",
    description="",
    charge=charge,
    provider=provider
)

coverage = CoverageFactory(
    code="ALI",
    vehicle_code="ABCD",
    charge=charge
)

vehicle = VehicleFactory(
    code="ECMM",
    description="GRUPO AX - MOVIDA ON",
    model="MOBI LIKE, OU SIMILAR",
    brand="",
    optionals=optional,
    coverages=coverage
)

当我使用pytest-django 运行测试时,我得到了这个错误。

ValueError: "<Vehicle: ECMM>" needs to have a value for field "id" before this many-to-many relationship can be used.

我已阅读有关 Simple Many-to-many relationshipMany-to-many relation with a ‘through’ 的 factory_boy 文档,但无法修复。

【问题讨论】:

    标签: python django factory-boy


    【解决方案1】:

    您已经在文档中指出了正确的位置,这应该可以工作:https://factoryboy.readthedocs.io/en/latest/recipes.html#simple-many-to-many-relationship

    class VehicleFactory(factory.Factory):
        ...
        # no optionals subfactory here
        ...
    
        @factory.post_generation
        def optionals(self, create, extracted, **kwargs):
            if not create:
                # Simple build, do nothing.
                return
    
            if extracted:
                # A list of groups were passed in, use them
                for optional in extracted:
                    self.groups.add(optional)
    

    然后你会打电话给:

    VehicleFactory.create(optionals=[optional1, optional2])
    

    希望这能回答问题吗?如果没有更多关于您在文档中尝试解决方案时不起作用的信息,很难提供更多帮助

    【讨论】:

      【解决方案2】:

      调用UserFactory()或UserFactory.build()时,没有组绑定 将被创建。

      但是当 UserFactory.create(groups=(group1, group2, group3)) 是 调用时,groups 声明会将传入的组添加到集合中 用户组。

      id 字段会在你生成时生成

      optional = OptionalFactory.create(  # note the .create()
          name="GPS",
          type="13",
          description="",
          charge=charge,
          provider=provider
      )
      

      那么当你

      vehicle = VehicleFactory.create(
          ...
          optionals=(optional,),
      )
      

      可以建立多对多optionals。还要注意可选参数的参数是(optionals,)。该函数期望一个可迭代的

      【讨论】: