1

我正在尝试为带有 GFK 的模型编写一个工厂进行测试,但我似乎无法让它工作。我参考了文档中的常见配方,但我的模型不完全匹配,而且我也遇到了错误。这是我的模型

class Artwork(models.Model):
    ...
    region = models.ForeignKey("Region", on_delete=models.SET_NULL, null=True, blank=True)

class Region(models.Model):
    # Could be either BeaconRegion or SpaceRegion
    region_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    region_object_id = models.PositiveIntegerField()
    region = GenericForeignKey("region_content_type", "region_object_id")


class SpaceRegion(models.Model):
    label = models.CharField(max_length=255)
    regions = GenericRelation(
        Region,
        content_type_field="region_content_type",
        object_id_field="region_object_id",
        related_query_name="space_region",
    )


class BeaconRegion(models.Model):
    label = models.CharField(max_length=255)
    regions = GenericRelation(
        Region,
        content_type_field="region_content_type",
        object_id_field="region_object_id",
        related_query_name="beacon_region",
    )

本质上, anArtwork可以放在两个Regions 之一中;一个SpaceRegionBeaconRegion

Factory为相应的模型创建了以下

class RegionFactory(factory.django.DjangoModelFactory):
    region_object_id = factory.SelfAttribute("region.id")
    region_content_type = factory.LazyAttribute(
        lambda o: ContentType.objects.get_for_model(o.region)
    )

    class Meta:
        exclude = ["region"]
        abstract = True


class BeaconRegionFactory(RegionFactory):
    label = factory.Faker("sentence", nb_words=2)
    region = factory.SubFactory(RegionFactory)

    class Meta:
        model = Region


class SpaceRegionFactory(RegionFactory):
    label = factory.Faker("sentence", nb_words=2)
    region = factory.SubFactory(RegionFactory)

    class Meta:
        model = Region


class ArtworkFactory(factory.django.DjangoModelFactory):
    ...
    region = factory.SubFactory(SpaceRegionFactory)

在我的测试中,我尝试使用创建艺术品ArtworkFactory(),但它与

AttributeError: The parameter 'region' is unknown. Evaluated attributes are {}, definitions are <DeclarationSet: {'region_object_id': <SelfAttribute('region.id', default=<class 'factory.declarations._UNSPECIFIED'>)>, 'region_content_type': <factory.declarations.LazyAttribute object at 0x1068cf430>, 'label': <factory.faker.Faker object at 0x1068cf880>}>

我在这里做错了什么?

4

1 回答 1

2

解决时出现问题ArtworkFactory.region.region,即SpaceRegionFactory.region

从您的模型看来:

  • Region是一个表,它指向SpaceRegionBeaconRegion
  • SpaceRegion并且BeaconRegion是简单的表,带有一个帮助器来检索相关Region对象。

在那些复杂的关系链中,第一步是编写没有工厂的代码:

>>> shire = SpaceRegion(label="Shire")
>>> shire_generic = Region(region=shire)
>>> the_ring = Artwork(region=shire_generic)

这告诉我们,Region总是在SpaceRegionor之后创建BeaconRegion,给出以下工厂:


class SpaceRegionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.SpaceRegion
    label = factory.Faker("sentence", n_words=2)


class RegionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Region

    region = factory.SubFactory(SpaceRegionFactory)


class ArtworkFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Artwork

    region = factory.SubFactory(RegionFactory)

有了这个,你应该能够让你的代码工作。请注意我们如何简单地将region字段设置为Region:Django 的内部将自动提取对象内容类型/内容 ID。

其他选项

您可以调整RegionFactory让调用者决定他们想要 aSpaceRegion还是 a BeaconRegion

class RegionFactory(factory.django.DjangoModelFactory):
    class Meta:
        models = Region

    class Params:
        space = True  # Request a SpaceRegion

    region = factory.Maybe(
        factory.SelfAttribute("space"),
        factory.SubFactory(SpaceRegion),
        factory.SubFactory(BeaconRegion),
    )
于 2021-01-28T16:15:50.497 回答