Избегайте дубликатов с завода 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 не пытался создавать новую страну каждый раз, когда создает адрес?
Ответы
Ответ 1
В последнем 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 является уникальным идентификатором.
Ответ 2
Пока вы правы, что нет функции get_or_create
для фабрик на базе SQLAlchemy, если объекты, которые вы хотите использовать в качестве внешнего ключа, уже существуют, вы можете перебирать их через:
http://factoryboy.readthedocs.org/en/latest/recipes.html#choosing-from-a-populated-table
Таким образом, вы могли бы взломать решение в своем factory с помощью ленивого атрибута, который сначала проверяет, существует ли объект в db, и если он использует этот метод для итерации через них, но если объект doesn ' t существует, он вызывает SubFactory
для создания объекта в первую очередь.
Ответ 3
Другим хакерским решением является перезаписать метод create
factory таким образом, чтобы объект искавался путем запроса и кеширования результатов.
Этот простой пример не фильтрует на **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
Ответ 4
Для 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',)