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

Django测试中FactoryBoy访问开发数据库而非测试库的问题

问题成因

你遇到的问题根源是 Python模块的加载时机和FactoryBoy属性的求值时机不匹配:

  • 你在定义ProductFactory的market_category字段时,直接执行了list(MarketplaceCategory.objects.all()),这段代码会在factories.py模块被导入的瞬间就执行完成,此时Django还没有启动测试流程、也没有切换到测试数据库,连接的依然是你的本地开发库,最终生成的选项列表里存的全是开发库的MarketplaceCategory记录,报错里出现的主键25就是开发库中的数据。
  • 测试运行时setUp方法里插入的是测试库的数据,而ProductFactory使用的还是模块导入阶段预生成的旧选项列表,自然会出现外键不存在的错误。
解决方案

有三个常用的修复方案,按需选择即可:

方案1:动态查询当前数据库的分类

使用LazyAttribute让查询逻辑延迟到工厂实例化时才执行,每次生成Product实例都会实时查询当前连接的数据库(测试阶段就是测试库)的分类数据:

# factories.py 修改后
class ProductFactory(DjangoModelFactory):
    ...
    # 每次实例化时才随机取当前库中存在的分类
    market_category = factory.LazyAttribute(lambda _: MarketplaceCategory.objects.order_by('?').first())

    class Meta:
        model = Product

注意:该方案需要保证调用ProductFactory前,数据库中已存在至少一条MarketplaceCategory记录,否则会返回空值触发非空约束报错。

方案2:显式指定关联对象

如果你的测试场景中需要复用setUp里创建的分类,直接在实例化工厂时手动传入即可,完全规避随机查询的问题:

# tests.py 修改后
def setUp(self) -> None:
    ...
    self.marketplace_category = MarketplaceCategoryFactory.create()
    self.product = ProductFactory(
        created_by=self.user,
        market_category=self.marketplace_category # 手动传入已创建的分类
    )

方案3:给FuzzyChoice传入可调用对象

如果你希望继续使用FuzzyChoice的语法,可将选项列表包裹为可调用对象,FactoryBoy会在实例化时才执行调用获取最新的选项:

# factories.py 修改后
class ProductFactory(DjangoModelFactory):
    ...
    # 传入lambda作为可调用对象,延迟执行查询
    market_category = factory.fuzzy.FuzzyChoice(lambda: list(MarketplaceCategory.objects.all()))

    class Meta:
        model = Product

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 06:00:04