【问题标题】:Django Factory for model with Generic Foreign Key具有通用外键的模型的 Django Factory
【发布时间】:2021-01-28 13:37:17
【问题描述】:

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

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",
    )

基本上,Artwork 可以放在两个Regions 之一中; SpaceRegionBeaconRegion

我为相应的模型创建了以下Factorys

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>}>

我在这里做错了什么?

【问题讨论】:

    标签: python django factory-boy generic-foreign-key


    【解决方案1】:

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

    从你的模型看来:

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

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

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

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

    
    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,让呼叫者决定他们想要SpaceRegion 还是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),
        )
    

    【讨论】:

    • 太棒了,效果很好!只是在factory.Maybe() 上的一个问题;我猜SpaceRegion 是默认值,如果没有传递任何其他内容。是否可以在另一个模型上将 BeaconRegion 指定为子工厂?一个人如何通过space=False kwarg?因为通常情况下,子工厂将被定义为 region = factory.SubFactory(RegionFactory)
    • @Andrew 你可以通过factory.SubFactory(RegionFactory, space=False)class Params 中描述的字段与其他声明一样被评估,唯一的例外是它们不会传递到底层模型:它们应该是 factory 的参数,正是space=True 在这个例子中的作用;)
    猜你喜欢
    • 2010-10-29
    • 1970-01-01
    • 2017-06-03
    • 2012-03-16
    • 2015-09-19
    • 2020-12-23
    • 2020-11-07
    • 2011-05-10
    • 1970-01-01
    相关资源
    最近更新 更多