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'

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


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


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

Поделиться с друзьями
-->

Комментарии (9)


  1. Wayfarer15
    18.01.2017 20:12
    +1

    Где-то тут должна быть ссылка на такое понятие как Data Masking, в особенности если данные используются для тестирования healthcare приложений. По этому поводу понаписано куча трудов, например — http://www.datamasker.com/DataMasking_WhatYouNeedToKnow.pdf


    1. likid_ri
      18.01.2017 20:17

      Спасибо за ссылку. Обязательно почитаю.


  1. antonksa
    18.01.2017 22:27

    Как я понял это ваша библиотека?


    1. likid_ri
      19.01.2017 08:53

      Если вас интересует создатель библиотеки, то это я, но принадлежит библиотека сообществу.


      1. antonksa
        19.01.2017 09:35

        Во-первых огромное спасибо! Прямо вот как только понадобилось, и Вы тут как тут :D
        Во-вторых, можете добавить middle_name или patronymic_name?, как же люди то без отчества, ощинама отчество надо, начальника!


        1. likid_ri
          19.01.2017 09:38

          Всегда пожалуйста! Да, конечно добавим.


  1. 3aicheg
    19.01.2017 04:34

    А в честь кого названа библиотека? Какой-то известной паталогической лгуньи?


    1. KhodeN
      19.01.2017 07:27

      Судя по картинке, это Элизабет, один из главных героев игры Bioshock Infinite.


      1. likid_ri
        19.01.2017 08:53

        Да, вы совершенно правы.