[英]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()
生成帶有關聯Report
的Sample
,而ReportFactory()
生成帶有2個關聯Samples
的Report
,沒有無限循環?
創建實例后, RelatedFactory
聲明進行評估:
Report
已實例化 SampleFactory
調用 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
,您:
Report
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.