factory_boy中的一对多关系

时间:2014-11-25 07:10:49

标签: python sqlalchemy factory-boy

我使用SQLalchemy作为我的ORM,并尝试将我的测试装置移植到factory_boy。我的架构包括一对多关系中的两个对象。即一个模型的实例具有类似于具有另一个模型的实例的列表。例如:

class Person(...):
  id = Column(Integer, primary_key=True)
  name = Column(Text)
  [...]

class Address(...):
  id = Column(Integer, primary_key=True)
  city = Column(Text)
  [...]
  person_id = Column(Integer, ForeignKey('person.id'))
  person = relationship("Person", backref="addresses")

现在我正在尝试创建一个可以创建具有多个地址的人的工厂。 Factory_boy有{​​{1}}。但我只看到你如何在一对一的关系中使用它。我知道我可以用一个单独的工厂创建地址然后附加它们,但我想做一些像SubFactory PersonFactory.create(num_addresses = 4)`。

有没有人知道这是否可以在factory_boy中使用?

我使用factory_boy 2.4.1。

5 个答案:

答案 0 :(得分:0)

我有这个确切的问题,并对这里缺乏好的答案感到失望。事实证明这是可能的!将此留给那些有同样问题的人。

首先,你的模型需要在ForeignKey上定义相反模型的关系,所以看起来应该是这样的:


class Person(...):
    id = Column(Integer, primary_key=True)
    name = Column(Text)
    addresses = relationship("Person", backref="person")
    [...]

class Address(...): id = Column(Integer, primary_key=True) city = Column(Text) [...] person_id = Column(Integer, ForeignKey('person.id'))

然后,在你的PersonFactory上,你可以像这样添加一个post_generation钩子:


class PersonFactory(BaseFactory):
    [...attributes...]

    @factory.post_generation
    def addresses(self, create, extracted, **kwargs):
        return AddressFactory.create_batch(4)

并替换' 4'无论你想要什么号码。显然,您还需要定义AddressFactory。

答案 1 :(得分:0)

目前,无法实现“多对一RelatedFactory”,以便“烘焙到您的工厂”......

也就是说,在实例化PersonFactory时,这种行为可以实施,并且有点hackery。

以下食谱将为您提供所需的信息:

from sqlalchemy import create_engine, Integer, Text, ForeignKey, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, scoped_session, sessionmaker
import factory
from factory.alchemy import SQLAlchemyModelFactory as sqla_factory
import random

engine = create_engine("sqlite:////tmp/factory_boy.sql")
session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()

class Person(Base):
    id = Column(Integer, primary_key=True)
    name = Column(Text)
    addresses = relationship("Address", backref="person")

class Address(Base):
    id = Column(Integer, primary_key=True)
    street = Column(Text)
    street_number = Column(Integer)
    person_id = Column(Integer, ForeignKey('person.id'))

class AddressFactory(sqla_factory):
    class Meta:
        model = Address
        sqlalchemy_session = session
    street_number = random.randint(0, 10000)
    street = "Commonwealth Ave"

class PersonFactory(sqla_factory):
    class Meta:
        model = Person
        sqlalchemy_session = session
    name = "John Doe"

Base.metadata.create_all(engine)
for i in range(100):
    person = PersonFactory(addresses=AddressFactory.create_batch(3))

答案 2 :(得分:0)

@Kristen指出了正确的方向,但是AdderssFactory与Person没有关系。 在 Django 中,我们可以像这样使用post_generation装饰器。

class PersonFactory(BaseFactory):
    @factory.post_generation
    def addresses(self, create, extracted, **kwargs):
        self addresses_set.add(AddressFactory(person=self))

答案 3 :(得分:0)

我在项目中使用了这种模式。假设您已经有AddressFactory

https://factoryboy.readthedocs.io/en/latest/reference.html?highlight=post_generation#factory.post_generation

class PersonFactory(factory.alchemy.SQLAlchemyFactory):
    class Meta:
        model = Person

    @factory.post_generation
    def addresses(obj, create, extracted, **kwargs):
        if not create:
            return

        if extracted:
            assert isinstance(extracted, int)
            AddressFactory.create_batch(size=extracted, person_id=self.id, **kwargs)

用法

PersonFactory(addresses=4)

这将创建具有4个Person的{​​{1}}

这也可以接受小矮人

Address

这将创建PersonFactory(addresses=2, addresses__city='London') ,其中包含2个Person字段,其中Address字段设置为city

以下是博客文章,可能会对https://simpleit.rocks/python/django/setting-up-a-factory-for-one-to-many-relationships-in-factoryboy/

有所帮助

答案 4 :(得分:-2)

您可以使用此处描述的解决方案:http://factoryboy.readthedocs.org/en/latest/recipes.html#reverse-dependencies-reverse-foreignkey

基本上,只需在RelatedFactory上声明一些PersonFactory

class PersonFactory(factory.alchemy.SQLAlchemyFactory):
    class Meta:
        model = Person

    address_1 = factory.RelatedFactory(AddressFactory, 'person')
    address_2 = factory.RelatedFactory(AddressFactory, 'person')