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

如何声明作为RelatedFactory属性的Factory属性?遇AttributeError求助

这问题我之前在处理Factory Boy双向外键关联的时候也踩过坑,给你几个实用的解决方案,既能实现双向关联,又能避免无限递归和AttributeError:

为什么你原来的代码会报错?

先理清楚原因:factory.RelatedFactory是在主实例(Valuation)创建并保存完成之后才会生成关联的LoanApplication实例的。而你用factory.SelfAttribute('loan.security')是在Valuation初始化阶段就试图访问loan属性,这时候loan还根本不存在,自然会抛出AttributeError: The parameter 'loan' is unknown。

另外,用SubFactory会触发无限递归,是因为如果LoanApplicationFactory里也默认创建Valuation的话,两个Factory会互相调用,陷入循环。


方案1:手动在post_generation中创建关联(最推荐)

这种方式完全控制关联流程,既避免递归,又能安全设置security属性:

首先,调整你的LoanApplicationFactory,让它默认不自动创建Valuation(切断递归链):

class LoanApplicationFactory(BEFactory):
    class Meta:
        model = LoanApplication

    # 不要默认设置valuation,避免自动触发ValuationFactory创建
    valuation = None
    # 其他字段定义...

然后修改ValuationFactory,用post_generation钩子手动创建并关联LoanApplication,同时设置security:

class ValuationFactory(BEFactory):
    class Meta:
        model = Valuation

    @factory.post_generation
    def loan(self, create, extracted, **kwargs):
        if not create:
            # 如果是模拟创建(不保存到数据库),直接返回
            return

        # 处理用户传入的自定义loan实例
        if extracted:
            self.loan = extracted
        else:
            # 创建LoanApplication并双向关联:把当前Valuation设为它的valuation
            self.loan = LoanApplicationFactory(valuation=self, **kwargs)
        
        # 现在loan已经存在,安全设置security
        self.security = self.loan.security
        # 保存更新后的Valuation
        self.save()

这个方案的优势:

  • 完全避免无限递归:因为LoanApplicationFactory不再自动创建Valuation,只有ValuationFactory主动创建LoanApplication并关联
  • 可以在post_generation中安全访问loan,因为是我们手动创建并立即关联的
  • 支持自定义传入loan实例,灵活性更高

方案2:用factory.RelatedFactory配合post_generation延迟设置security

如果你坚持要用RelatedFactory,可以通过在post_generation中延迟获取并设置security,但需要注意RelatedFactory的执行时机:

class ValuationFactory(BEFactory):
    class Meta:
        model = Valuation

    # 用RelatedFactory创建LoanApplication,关联到当前Valuation
    loan = factory.RelatedFactory(LoanApplicationFactory, 'valuation')

    @factory.post_generation
    def set_security(self, create, extracted, **kwargs):
        if not create:
            return
        
        # 因为RelatedFactory是在主实例保存后创建的,这里需要手动刷新实例
        # 或者直接通过反向查询获取关联的LoanApplication
        self.loan = self.loanapplication_set.first()  # 注意:这里的反向关联名称要对应你的模型定义
        self.security = self.loan.security
        self.save()

注意事项:

  • 你需要确认Django反向关联的名称(默认是小写模型名_set,如果模型里设置了related_name,要改成对应的名称)
  • 这个方案不如方案1直观,可读性稍差

额外提示:避免无限递归的通用技巧

在处理双向外键的Factory时,永远确保只有一个Factory主动创建关联对象,另一个Factory的关联字段默认设为None或者可选。这样就能切断递归链,不会出现互相调用的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:37:46