首页 > 解决方案 > 与工厂男孩的一对多关系

问题描述

我的 SQLAlchemy 模型中有一个多对一的关系。一份报告有许多样本(为简洁起见进行了简化):

class Sample(db.Model, CRUDMixin):
    sample_id = Column(Integer, primary_key=True)
    report_id = Column(Integer, ForeignKey('report.report_id', ondelete='CASCADE'), index=True, nullable=False)
    report = relationship('Report', back_populates='samples')

class Report(db.Model, CRUDMixin):
    report_id = Column(Integer, primary_key=True)
    samples = relationship('Sample', back_populates='report')

现在在我的测试中,我希望能够生成一个Sample实例,或者一个Report实例,并填充缺失的关系。

class ReportFactory(BaseFactory):
    class Meta:
        model = models.Report
    report_id = Faker('pyint')
    samples = RelatedFactoryList('tests.factories.SampleFactory', size=3)

class SampleFactory(BaseFactory):
    class Meta:
        model = models.Sample
    sample_id = Faker('pyint')
    report = SubFactory(ReportFactory)

当我去创建这些实例时,工厂陷入了无限循环:

RecursionError: maximum recursion depth exceeded in comparison

但是,如果我尝试使用SelfAttributes 来停止无限循环,我最终会得到一个没有任何样本的报告:

class ReportFactory(BaseFactory):
    samples = RelatedFactoryList('tests.factories.SampleFactory', size=3, report_id=SelfAttribute('..report_id'))

class SampleFactory(BaseFactory):
    report = SubFactory(ReportFactory, samples=[])
report = factories.ReportFactory()
l = len(report.samples) # 0

但是,如果我生成一个Samplewith SampleFactory(),它正确地有一个Report对象。

我应该如何正确设计我的工厂,以便SampleFactory()生成一个Samplewith associated Report,并ReportFactory()生成一个Reportwith 2 associated Samples,而没有无限循环?

标签: pythonsqlalchemyfactory-boy

解决方案


我的最终解决方案实际上比我想象的要简单得多:

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=[])

关键是使用第二个参数 toRelatedFactoryList,在这种情况下,它必须对应于子级的父链接'report'。此外,我使用SubFactory(ReportFactory, samples=[])了 ,它确保如果我构建单个样本,不会在父节点上创建额外的样本。

使用此设置,我可以构建一个Report与之关联的样本,并且该报告只有 1 个 child Sample。相反,我可以构建一个Report将自动填充 3 个子样本的。

我认为没有必要生成实际的模型 ID,因为一旦模型实际插入数据库,SQLAlchemy 就会自动执行此操作。但是,如果您想在不使用数据库的情况下执行此操作,我认为@Xelnor 的解决方案report_id = factory.SelfAttribute('report.id')将起作用。

我遇到的唯一未解决的问题是覆盖报告上的样本列表(例如ReportFactory(samples = [SampleFactory()])),但我已经打开了一个记录此错误的问题:https ://github.com/FactoryBoy/factory_boy/issues/636


推荐阅读