具有通用外键的模型的 Django 工厂
Django Factory for model with Generic Foreign Key
我正在尝试为带有 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
可以放在两个Region
之一中;一个 SpaceRegion
或 BeaconRegion
.
我为相应的模型创建了以下 Factory
s
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>}>
我做错了什么?
解决 ArtworkFactory.region.region
时出现问题,即 SpaceRegionFactory.region
。
从您的模型看来:
Region
是指向 SpaceRegion
或 BeaconRegion
的 table
SpaceRegion
和 BeaconRegion
是简单的 tables,有一个帮助程序来检索相关的 Region
对象。
那些复杂关系链中的第一步是编写没有工厂的代码:
>>> shire = SpaceRegion(label="Shire")
>>> shire_generic = Region(region=shire)
>>> the_ring = Artwork(region=shire_generic)
这告诉我们 Region
总是在 SpaceRegion
或 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
让呼叫者决定他们想要 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),
)
我正在尝试为带有 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
可以放在两个Region
之一中;一个 SpaceRegion
或 BeaconRegion
.
我为相应的模型创建了以下 Factory
s
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>}>
我做错了什么?
解决 ArtworkFactory.region.region
时出现问题,即 SpaceRegionFactory.region
。
从您的模型看来:
Region
是指向SpaceRegion
或BeaconRegion
的 table
SpaceRegion
和BeaconRegion
是简单的 tables,有一个帮助程序来检索相关的Region
对象。
那些复杂关系链中的第一步是编写没有工厂的代码:
>>> shire = SpaceRegion(label="Shire")
>>> shire_generic = Region(region=shire)
>>> the_ring = Artwork(region=shire_generic)
这告诉我们 Region
总是在 SpaceRegion
或 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
让呼叫者决定他们想要 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),
)