Python 与工厂男孩的一对多关系
在我的SQLAlchemy模型中,我有一个多对一的关系。一份报告包含多个示例(为简洁起见,进行了简化):Python 与工厂男孩的一对多关系,python,sqlalchemy,factory-boy,Python,Sqlalchemy,Factory Boy,在我的SQLAlchemy模型中,我有一个多对一的关系。一份报告包含多个示例(为简洁起见,进行了简化): 类示例(db.Model,CRUDMixin): 样本id=列(整数,主键=真) report\u id=Column(整数,ForeignKey('report.report\u id',ondelete='CASCADE'),index=True,nullable=False) 报告=关系('report',back_populates='samples') 课堂报告(db.Model,
类示例(db.Model,CRUDMixin):
样本id=列(整数,主键=真)
report\u id=Column(整数,ForeignKey('report.report\u id',ondelete='CASCADE'),index=True,nullable=False)
报告=关系('report',back_populates='samples')
课堂报告(db.Model,CRUDMixin):
报告id=列(整数,主键=真)
样本=关系('Sample',back_populates='report')
现在在我的测试中,我希望能够生成一个示例
实例,或者一个报告
实例,并填充缺少的关系
类报告工厂(BaseFactory):
类元:
模型=模型。报告
报告\u id=Faker('pyint'))
samples=RelatedFactoryList('tests.factories.SampleFactory',size=3)
类别样本工厂(BaseFactory):
类元:
model=models.Sample
样本_id=Faker('pyint'))
报表=子工厂(报表工厂)
当我去创建这些实例时,工厂陷入无限循环:
RecursionError: maximum recursion depth exceeded in comparison
但是,如果我尝试使用SelfAttribute
s来停止无限循环,我最终会得到一个没有任何示例的报告:
类报告工厂(BaseFactory):
samples=RelatedFactoryList('tests.factories.SampleFactory',size=3,report\u id=SelfAttribute(“…report\u id”))
类别样本工厂(BaseFactory):
报告=子工厂(报告工厂,样本=[])
但是,如果我使用SampleFactory()
生成Sample
,则它正确地具有一个报告
对象
我应该如何正确地设计我的工厂,以便
SampleFactory()
将生成带有关联报告的样本
,而ReportFactory()
将生成带有两个关联样本的报告
,没有无限循环?创建实例后,将对RelatedFactory
声明进行评估:
将实例化报告
执行3次调用SampleFactory
返回步骤1中实例化的报告
为了填充报告
实例上的字段,必须在步骤2将示例
实例链接到报告
一种可能的实施办法是:
class SampleFactory(BaseFactory):
class Meta:
model = Sample
@classmethod
def _after_postgeneration(cls, instance, create, results=None):
if instance.report is not None and instance not in instance.report.samples:
instance.report.samples.append(instance)
id = factory.Faker('pyint')
# Enfore `post_samples = None` to prevent creating additional samples
report = factory.SubFactory('example.ReportFactory', samples=[], post_samples=None)
report_id = factory.SelfAttribute('report.id')
class ReportFactory(factory.Factory):
class Meta:
model = Report
id = factory.Faker('pyint')
# Set samples = [] if needed by `Report.__init__`
samples = []
# Named `post_samples` to mark that they are instantiated
# *after* the `Report` is ready (and never passed to the `samples` kwarg)
post_samples = factory.RelatedFactoryList(SampleFactory, 'report', size=3)
使用该代码,当您调用ReportFactory
时,您:
生成不带任何样本的报告
生成3个示例,向它们传递对刚刚生成的报告的引用
创建时,这些Sample
实例将自己附加到Report.samples
我的最终解决方案实际上比我想象的要简单得多:
class ReportFactory(BaseFactory):
class Meta:
model = models.Report
samples = RelatedFactoryList('tests.factories.SampleFactory', 'report', size=3)
class SampleFactory(BaseFactory):
class Meta:
model = models.Sample
report = SubFactory(ReportFactory, samples=[])
关键是使用RelatedFactoryList
的第二个参数,该参数必须对应于子对象上的父链接,在本例中为'report'
。此外,我还使用了子工厂(ReportFactory,samples=[])
,这确保了在构建单个样本时不会在父级上创建额外的样本
通过此设置,我可以构造一个示例,该示例将有一个与之关联的报告
,并且该报告只有一个子示例
。相反,我可以构建一个报告
,该报告将自动填充3个子样本
我认为不需要生成实际的模型ID,因为一旦模型实际插入到数据库中,SQLAlchemy就会自动生成模型ID。但是,如果您想在不使用数据库的情况下实现这一点,我认为@Xelnor的report\u id=factory.SelfAttribute('report.id')
解决方案是可行的
我唯一未解决的问题是覆盖报表上的样本列表(例如,ReportFactory(samples=[SampleFactory()])
),但我已经打开了一个记录此错误的问题:但是自从您命名了RelatedFactoryList
发布样本之后,生成的报表
中不会有示例
吗?不会,因为我们在创建报表时,会在\u postgeneration
钩子中手动将每个示例
附加到其报表
中。这也很有效,但仅是因为ORM的基本功能:当您阅读报表时。示例
,SQLAlchemy将动态获取数据库(或会话)中指向特定报告的样本对象列表。如果您没有使用ORM,您必须手动链接它们。感谢您的澄清。不过,我在问题中确实提到了炼金术。
class ReportFactory(BaseFactory):
class Meta:
model = models.Report
samples = RelatedFactoryList('tests.factories.SampleFactory', 'report', size=3)
class SampleFactory(BaseFactory):
class Meta:
model = models.Sample
report = SubFactory(ReportFactory, samples=[])