简体   繁体   English

factory_boy 中的一对多关系

[英]One-to-many relationships in factory_boy

I use SQLalchemy as my ORM and am trying to port my test fixtures to factory_boy.我使用 SQLalchemy 作为我的 ORM,并试图将我的测试装置移植到 factory_boy。 My schema includes two objects in a one-to-many relation.我的模式包括一对多关系中的两个对象。 Ie instances of one model have list like structures with instances of the other.即一个模型的实例具有类似结构的列表,其中包含另一个模型的实例。 Example:例子:

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")

Now I am trying to create a factory which creates persons with a couple of addresses.现在我正在尝试创建一个工厂,该工厂创建具有几个地址的人员。 Factory_boy has the SubFactory . SubFactorySubFactory But I only see how you can use that in a one-to-one relationship.但我只看到你如何在一对一的关系中使用它。 I know I can create the addresses with a separate factory and then attach them, but I would like to do something like person = PersonFactory.create(num_addresses=4)`.我知道我可以使用单独的工厂创建地址然后附加它们,但我想做类似person = PersonFactory.create(num_addresses=4)` 的事情。

Does anyone know if this is currently possible in factory_boy?有谁知道这在 factory_boy 中目前是否可行?

I use factory_boy 2.4.1.我使用 factory_boy 2.4.1。

I am using this pattern in my project.我在我的项目中使用这种模式。 Assuming you already have AddressFactory .假设您已经拥有AddressFactory

https://factoryboy.readthedocs.io/en/latest/reference.html?highlight=post_generation#factory.post_generation 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)

Usage用法

PersonFactory(addresses=4)

This will create Person with 4 Address es这将创建具有 4 个Address Person

Also this can accept kwargs这也可以接受 kwargs

PersonFactory(addresses=2, addresses__city='London')

This will create Person with 2 Address es which have city field set to 'London'这将创建具有 2 个Address Person ,其city字段设置为'London'

Here is blog post which may help https://simpleit.rocks/python/django/setting-up-a-factory-for-one-to-many-relationships-in-factoryboy/这是可能有帮助的博客文章https://simpleit.rocks/python/django/setting-up-a-factory-for-one-to-many-relationships-in-factoryboy/

I had this exact question and was disappointed in the lack of good answers here.我有这个确切的问题,并对这里缺乏好的答案感到失望。 Turns out it is possible!事实证明这是可能的! Leaving this here for those who have the same question.把这个留在这里给那些有同样问题的人。

First, your model needs to define the relationship on the opposite model from the ForeignKey, so it should look like:首先,您的模型需要从 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'))

Then, on your PersonFactory, you can add a post_generation hook like this:然后,在您的 PersonFactory 上,您可以添加一个 post_generation 钩子,如下所示:


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

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

and replace the '4' with whatever number you want. 并用您想要的任何数字替换“4”。 Obviously, you need to define the AddressFactory as well. 显然,您还需要定义 AddressFactory。

Currently, there is no way to implement a "many-to-one RelatedFactory " such that it is " baked into your factory "...目前,没有办法实现“多对一的相关RelatedFactory ”,使其“融入您的工厂”......

That said, this behavior can be implemented with a bit of hackery when instantiating your PersonFactory .也就是说,在实例化您的PersonFactory时,可以通过一些PersonFactory来实现此行为。

The following recipe will get you what you are looking for:以下食谱将为您提供所需的东西:

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))

@Kristen pointed to the right direction, but AdderssFactory didn't related to Person. @Kristen 指出了正确的方向,但 AdderssFactory 与 Person 无关。 In Django we can use post_generation decorator like this.Django 中,我们可以像这样使用post_generation装饰器。

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

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

Basically, just declare a few RelatedFactory on your PersonFactory :基本上,只是声明了几个RelatedFactoryPersonFactory

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM