避免与factory_boy工厂重复

时间:2013-10-02 21:26:18

标签: python sqlalchemy factory-boy

我使用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每次创建地址时都不会尝试创建新的国家/地区?

5 个答案:

答案 0 :(得分:6)

在最新的工厂男孩== 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字段是唯一标识符。

答案 1 :(得分:3)

While you're right that there's no get_or_create function for SQLAlchemy-based factories, if the objects you want to use as a foreign key already exist, you can iterate through them:

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

So conceivably you could hack together a solution in your factory by using a lazy attribute that first checks if the object exists in the db, and if so it uses this method to iterate through them, but if the object doesn't exist, it calls a SubFactory to create the object first.

答案 2 :(得分:0)

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

这个简单的例子虽然没有对**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                                             

答案 3 :(得分:0)

对于SqlAlchemy,您可以尝试此操作。这也是cahce工厂:

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

答案 4 :(得分: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属性,该属性使用数据库中已经创建的country实例创建一个地址实例。