Генерация фиктивных данных с Elizabeth: Часть II

в 15:59, , рубрики: db, django, dummy, fake, python, sqlite, testing, данные, тест, фейк, фиктивные
image

Ранее я уже публиковал статью о том, как генерировать фиктивные данные при помощи Elizabeth — библиотеки для языка программирования Python. Статья, которую вы читаете является продолжением предыдущей, потому я не буду приводить основ работы с библиотекой. Если вы пропустили статью, поленились прочитать или просто не захотели, то, вероятно, захотите сейчас, ибо эта статья подразумевает, что читатель уже знаком с основами библиотеки. В этой части статьи я буду говорить о том, каким образом организовывать генерацию фиктивных данных в собственных приложениях, расскажу о нескольких, на мой взгляд, полезных особенностях библиотеки.

Рекомендации

Прежде всего хотелось бы отметить, что Elizabeth не разрабатывалась для использования с конкретной БД или ORM. Основная задача, которую библиотека решает — это предоставление валидных данных. Строгих правил работы с библиотекой нет, но есть рекомендации, которые помогут держать ваше тестовое окружение в порядке. Рекомендации довольно просты и соответствуют духу Python.

Структурирование

Функции, выполняющие генерацию данных и их запись в БД, необходимо держать рядом с моделями, а еще лучше, как статические методы модели к которой они относятся, по примеру метода _bootstrap() из предыдущей статьи. Это нужно во избежание беготни по файлам, когда меняется структура модели и необходимо добавить какое-то новое поле.

class Patient(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True)
    phone_number = db.Column(db.String(25))
    full_name = db.Column(db.String(100))
    weight = db.Column(db.String(64))
    height = db.Column(db.String(64))
    blood_type = db.Column(db.String(64))

    def __init__(self, **kwargs):
        super(Patient, self).__init__(**kwargs)

    @staticmethod
    def _bootstrap(count=2000):
        from elizabeth import Personal

        person = Personal('en')
        for _ in range(count):
            patient = Patient(email=person.email(),
                              phone_number=person.telephone(),
                              full_name=person.full_name(gender='female'),
                              weight=person.weight(),
                              height=person.height(),
                              blood_type=person.blood_type()
                              )

            db.session.add(patient)
            try:
                db.session.commit()
            except Exception:
                db.session.rollback()

Создание объектов

Если ваше приложение ожидает данные на одном конкретном языке и только на нем, то лучше всего использовать класс Generic, а не создавать множество экземпляров классов-поставщиков по отдельности. Используя Generic вы избавитесь от лишних строк кода.

Верно:

>>> from elizabeth import Generic

>>> generic = Generic('ru')
>>> generic.personal.username()
'sherley3354'
>>> generic.datetime.date()
'14-05-2007'

Неверно:

>>> from elizabeth import Personal, Datetime, Text, Code

>>> personal = Personal('ru')
>>> datetime = Datetime('ru')
>>> text = Text('ru')
>>> code = Code('ru')

В то же время верно:

>>> from elizabeth import Personal

>>> p_en = Personal('en')
>>> p_sv = Personal('sv')
>>> # ...

Запись данных в БД

Если вам нужно сгенерировать данные и записать их в БД, то рекомендуется генерировать данные порциями, а не разом 600k. Необходимо помнить, что могут быть какие-то ограничения со стороны базы данных. Чем меньше порции данных, генерирумые для записи, тем быстрее запись.

>>> User()._bootstrap(count=2000, locale='de')

Пользовательские провайдеры

Библиотека поддерживает большое количество данных и в большинстве случаев их будет достаточно, однако для тех, кто хочет создать свои провайдер с более специфичными данными, такая возможность поддерживается и делается это следующим образом:

>>> from elizabeth import Generic

>>> generic = Generic('en')

>>> class SomeProvider():
        class Meta:
            name = 'some_provider'

        def ints(self):
            return [i for i in range(1, 5)]

>>> class Another():
        def bye(self):
            return "Bye!"

>>> generic.add_provider(SomeProvider)
>>> generic.add_provider(Another)

>>> generic.some_provider.ints()
[1, 2, 3, 4]

>>> generic.another.bye()
'Bye!'

Builtin providers

Большинство стран, где тот или иной язык является официальным, имеют данные, которые характерны только для этих стран. К примеру CPF для Бразилии, SSN для США. Такого рода данные могут причинять неудобства и нарушать порядок (или как минимум раздражать) тем, что будут присутствовать во всех объектах, независимо от выбранного языкового стандарта. Вы можете сами убедиться в сказанном, если посмотрите на пример, того, как это выглядело бы (код работать не будет).

>>> from elizabeth import Personal

>>> person = Personal('ru')
>>> person.ssn()
>>> person.cpf()

Думаю многие согласятся с тем, что это выглядит совсем нехорошо.

Такие данные должны быть доступны только для конкретных языков, потому классы-провайдеры, предоставляющие такого рода специфичные данные вынесены в отдельный подпакет (elizabeth.builtins), чтобы сохранить общую для всех языков структуру классов и их объектов.

Так это работает:

>>> from elizabeth import Generic
>>> from elizabeth.builtins import Brazil

>>> generic = Generic('pt-br')

>>> class BrazilProvider(Brazil):
        class Meta:
            name = "brazil_provider"

>>> generic.add_provider(BrazilProvider)
>>> generic.brazil_provider.cpf()
'001.137.297-40'

В каких данные чаще всего возникает необходимость в вашей работе? Что в библиотеке упустили и что необходимо немедленно добавить? Мы были бы очень рады услышать ваши пожелания/рекомендации/замечания.

Ссылка на проект: тут.
На документацию ссылка: тут.
На первую часть статьи: тут.

На этом у меня все, друзья. Вам удачных тестов и да пребудет с вами сила!

Автор: likid_ri

Источник

Поделиться

* - обязательные к заполнению поля