Перейти к содержанию

Фабрики

Фабрика создаёт объекты модели для тестов: каждое поле — из объявления, а значение, переданное в вызов, сильнее объявления. Модель — аргумент типа фабрики:

from hare.contrib.factories import LazyAttribute, ManyToMany, ModelFactory, RelatedFactory, Sequence, SubFactory, Trait


class PublisherFactory(ModelFactory[Publisher]):
    name = Sequence(lambda number: f"publisher{number}")


class AuthorFactory(ModelFactory[Author]):
    name = Sequence(lambda number: f"author{number}")
    email = LazyAttribute(lambda author: f"{author.name}@{author.domain}")
    publisher = SubFactory(PublisherFactory)
    tags = ManyToMany(TagFactory, size=2)
    books = RelatedFactory("library.factories.BookFactory", "author", size=3)

    class Params:
        domain = "example.com"
        featured = Trait(is_featured=True)


author = await AuthorFactory.create(name="ann")        # сохранён вместе с издательством, тегами и книгами
featured = await AuthorFactory.create(featured=True)   # значения признака
draft = AuthorFactory.build(publisher=publisher)       # не сохранён; связанный объект только переданный
authors = await AuthorFactory.create_batch(10)
drafts = AuthorFactory.build_batch(3)

Объявления

Объявление Значение каждого объекта
обычное значение Само значение; список, словарь или множество копируется для каждого объекта, так что изменение у одного объекта не меняет другие.
Sequence(function) function(number) — номер объекта в последовательности его фабрики, с 0 (reset_sequence() начинает её заново). Фабрика-наследник другой фабрики той же модели продолжает нумерацию в этой последовательности — это строки одной таблицы.
LazyFunction(function) function().
LazyAttribute(function) function(values) — остальные значения объекта как атрибуты, параметры из Params среди них. Вычисляется после всех остальных значений, в порядке объявления.
Iterator(values, *, cycle=True) Следующее из значений; когда они кончаются — снова с начала; с cycle=False объект, созданный после последнего значения, даёт ConfigurationError.
Faker(provider, *, locale=None, **arguments) Значение провайдера библиотеки faker (pip install faker; без неё — ConfigurationError).
SubFactory(factory, **values) Объект другой фабрики, создаётся вместе с объектом; несохранённый объект его не получает: модель принимает только сохранённый связанный объект, переданный в build(). Подходит и для цели GenericForeignKeyField.
RelatedFactory(factory, related_name, *, size=1, **values) После создания объекта — size строк другой фабрики, чьё поле related_name указывает на него; число, переданное под именем объявления, заменяет size.
ManyToMany(factory=None, *, size=0, **values) После создания объекта — связи с size объектами другой фабрики; объекты, переданные под его именем, связываются вместо них.
Trait(**values) в Params Значения, когда параметр передан истинным.

Своё объявление, которое действует после создания объекта, наследуется от PostDeclaration (hare.contrib.factories) и реализует async def apply(self, obj, name, given) -> None: name — его имя на фабрике, given — что create() получил под этим именем (None, если ничего). Объект build() не сохраняется и ничего из этого не получает.

Имена, наследование и арендаторы

Фабрику можно назвать путём через точку — для двух фабрик, ссылающихся друг на друга. Фабрика наследует объявления фабрики, от которой унаследована. Поле Meta.tenant_field модели получает арендатора активного Tenancy.scope(), если оно не передано; составной первичный ключ объявляется поле за полем.

Пакеты объектов

create_batch() вставляет объекты одним bulk_create(), если ни один из них не создаёт связанный объект и ничего не делает после создания (SubFactory, RelatedFactory, ManyToMany), а база возвращает сгенерированные первичные ключи в порядке вставки (RETURNING, PostgreSQL); иначе создаёт их по одному — у каждого возвращённого объекта есть первичный ключ. build_batch() собирает несохранённые объекты так же, как build(). Несохранённый объект не получает RelatedFactory/ManyToMany.