如何声明作为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

