19

我正在使用factory_boy创建测试夹具。我有两个简单的工厂,由 SQLAlchemy 模型支持(简化如下)。

我希望能够AddressFactory.create()多次调用,Country如果它不存在则让它创建一个,否则我希望它重新使用现有记录。

class CountryFactory(factory.Factory):
    FACTORY_FOR = Country

    cc = "US"
    name = "United States"


class AddressFactory(factory.Factory):
    FACTORY_FOR = Address

    name = "Joe User"
    city = "Seven Mile Beach"
    country = factory.SubFactory(CountryFactory, cc="KY", name="Cayman Islands")

我的问题是:如何设置这些工厂,以便 factory_boy 不会在每次创建地址时都尝试创建新国家?

4

5 回答 5

9

在最新的 factory-boy==2.3.1 中可以添加 FACTORY_DJANGO_GET_OR_CREATE

class CountryFactory(factory.django.DjangoModelFactory):
    FACTORY_FOR = 'appname.Country'
    FACTORY_DJANGO_GET_OR_CREATE = ('cc',)

    cc = "US"
    name = "United States"

假设 cc 字段是唯一标识符。

于 2014-04-02T06:57:28.417 回答
4

虽然你是对的,基于 SQLAlchemy 的工厂没有get_or_create函数,但如果你想用作外键的对象已经存在,你可以遍历它们:

http://factoryboy.readthedocs.org/en/latest/recipes.html#choosing-from-a-populated-table

因此可以想象,您可以通过使用惰性属性在您的工厂中组合一个解决方案,该属性首先检查对象是否存在于数据库中,如果存在,则使用此方法遍历它们,但如果对象不存在,则调用aSubFactory先创建对象。

于 2015-08-07T01:48:29.407 回答
1

对于 SqlAlchemy,你可以试试这个。这也是缓存工厂:

class StaticFactory(factory.alchemy.SQLAlchemyModelFactory):):
    __static_exclude = ('__static_exclude', '__static_cache',)
    __static_cache = {}
 
    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        """Helper for avoid duplicate factory"""
 
        # Exclude static cache
        cls._meta.exclude += cls.__static_exclude
 
        _unique_key = None
 
        # Get first unique keys from table. I'll be cache key.
        for col in model_class.__table__.columns:
            if any([col.primary_key, col.unique]):
                _unique_key = kwargs.get(col.name)
                if _unique_key:
                    break
 
        _instance = cls.__static_cache.get(_unique_key)
        if _instance:
            return _instance
 
        _session = cls._meta.sqlalchemy_session
        with _session.no_autoflush:
            obj = model_class(*args, **kwargs)
            _session.add(obj)
            cls.__static_cache.update({_unique_key: obj})
            return obj

class LanguageFactory(StaticFactory):
    class Meta:
        model = Language
        exclude = ('lang',)
于 2019-05-09T10:13:52.513 回答
0

create另一个 hacky 解决方案是以通过查询和缓存结果来搜索对象的方式覆盖工厂的方法。

这个简单的例子没有过滤**kwargs

class StaticFactory(SQLAlchemyModelFactory):                        

    counter = 0                                                     
    cache = []                                                      
    model = None                                                    

    @classmethod                                                    
    def create(cls, **kwargs):                                      
        if not cls.cache:                                           
            cls.cache = your_session.query(cls.model).all()     
        instance = cls.cache[cls.counter]                           
        cls.counter = (cls.counter + 1) % len(cls.cache)            
        return instance                                             
于 2017-03-31T07:51:46.903 回答
0

我们可以使用factory.Iterator方法使用已经存在的国家/地区实例创建一个新的地址实例

import factory, factory.django
from . import models


class CountryFactory(factory.Factory.DjangoModelFactory):
    model = models.Country

    cc = "US"
    name = "United States"

class AddressFactory(factory.Factory.DjangoModelFactory):
    model = models.Address

    name = "Joe User"
    city = "Seven Mile Beach"
    country = factory.Iterator(models.Country.objects.all())

在这里,我们从数据库中访问 Country 实例并将其传递给 AddressFactory 的 country 属性,该属性在数据库中创建一个地址实例,其中包含已创建的国家/地区实例。

于 2020-06-23T18:22:02.420 回答