簡體   English   中英

與工廠男孩一對多的關系

[英]One to many relation with Factory Boy

我的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

但是,如果嘗試使用SelfAttribute停止無限循環,則最終得到的報告將不包含任何樣本:

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

但是,如果我用SampleFactory()生成一個Sample ,則它正確地具有一個Report對象。

如何正確設計工廠,使SampleFactory()生成帶有關聯ReportSample ,而ReportFactory()生成帶有2個關聯SamplesReport ,沒有無限循環?

創建實例后, RelatedFactory聲明進行評估:

  1. Report已實例化
  2. 進行了3次對SampleFactory調用
  3. 返回在步驟1中實例化的Report

為了填充Report實例上的字段,您必須在步驟2將Sample實例鏈接Report

可能的實現方式是:

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 ,您:

  1. 生成沒有任何樣本的Report
  2. 生成3個樣本,並將它們傳遞給剛剛生成的報告的參考
  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' 另外,我使用了SubFactory(ReportFactory, samples=[]) ,這可以確保在構造單個樣本時不會在父項上創建任何額外的樣本。

通過此設置,我可以構造一個樣本,該樣本將具有與之關聯的Report ,並且該報告只有1個子Sample 相反,我可以構建一個Report ,該Report將自動填充3個子樣本。

我認為不需要生成實際的模型ID,因為一旦將模型實際插入數據庫中,SQLAlchemy就會自動執行。 但是,如果您想在不使用數據庫的情況下執行此操作,我認為@Xelnor的report_id = factory.SelfAttribute('report.id')解決方案將起作用。

我唯一的未解決的問題是覆蓋Report上的樣本列表(例如ReportFactory(samples = [SampleFactory()]) ),但是我打開了一個記錄此bug的問題: https : //github.com/FactoryBoy / factory_boy /問題/ 636

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM