You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Factoryboy无法生成数据库依赖字段,django-cities-light测试场景如何解决?

问题根源

你写的工厂类存在两个核心问题:

  1. FuzzyChoice(Country.objects.all()) 会在类定义阶段就执行Country.objects.all()查询,测试环境类加载时数据库还没导入数据,拿到空序列直接触发报错,or None的兜底逻辑完全不会生效。
  2. 没有处理测试环境无基础地理数据的 fallback 逻辑,关联字段依赖上层关联对象存在,空值会触发连锁报错。

方案1:测试环境预填充地理数据(推荐贴近真实业务场景)

先在测试初始化阶段导入django-cities-light的基础数据集,可以用django的setUpTestData或者pytest全局fixture执行导入逻辑,再修改工厂类把查询延迟到实例生成阶段:

class UserProfileFactory(DjangoModelFactory):
    class Meta:
        model = UserProfile

    # 传入callable让FuzzyChoice在实例生成时再执行查询
    birth_country = factory.fuzzy.FuzzyChoice(lambda: Country.objects.all())
    # 增加空值兜底,避免上层字段为空时报错
    birth_region = factory.LazyAttribute(
        lambda o: o.birth_country.region_set.order_by("?").first() if o.birth_country else None
    )
    birth_subregion = factory.LazyAttribute(
        lambda o: o.birth_region.subregion_set.order_by("?").first() if o.birth_region else None
    )
    birth_city = factory.LazyAttribute(
        lambda o: o.birth_region.city_set.order_by("?").first() if o.birth_region else None
    )

方案2:完全自包含的测试工厂(不需要提前导入数据,适合单元测试)

先给地理模型创建对应的工厂类,没有现成数据时自动生成假数据,适配任何环境:

# 先定义基础地理数据工厂,按django-cities-light的模型必填字段补充参数即可
class CountryFactory(DjangoModelFactory):
    class Meta:
        model = Country
    name = factory.Faker("country")
    code2 = factory.Faker("pystr", min_chars=2, max_chars=2, prefix="", suffix="", letters="ABCDEFGHIJKLMNOPQRSTUVWXYZ")
    # 其他必填字段按需补充

class RegionFactory(DjangoModelFactory):
    class Meta:
        model = Region
    name = factory.Faker("state")
    country = factory.SubFactory(CountryFactory)

class SubRegionFactory(DjangoModelFactory):
    class Meta:
        model = SubRegion
    name = factory.Faker("state")
    region = factory.SubFactory(RegionFactory)

class CityFactory(DjangoModelFactory):
    class Meta:
        model = City
    name = factory.Faker("city")
    region = factory.SubFactory(RegionFactory)

# 改造后的UserProfileFactory,有现成数据用现成的,没有就自动创建
class UserProfileFactory(DjangoModelFactory):
    class Meta:
        model = UserProfile

    birth_country = factory.LazyFunction(
        lambda: Country.objects.order_by("?").first() or CountryFactory()
    )
    birth_region = factory.LazyAttribute(
        lambda o: o.birth_country.region_set.order_by("?").first() or RegionFactory(country=o.birth_country)
    )
    birth_subregion = factory.LazyAttribute(
        lambda o: o.birth_region.subregion_set.order_by("?").first() or SubRegionFactory(region=o.birth_region)
    )
    birth_city = factory.LazyAttribute(
        lambda o: o.birth_region.city_set.order_by("?").first() or CityFactory(region=o.birth_region)
    )

可选优化

如果不想大量使用LazyAttribute,可以把关联字段的生成逻辑封装为PostGeneration钩子,或者自定义factory_boy的Provider,复用逻辑。

内容的提问来源于stack exchange,提问作者aladagemre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 03:27:02