Ранее я уже публиковал статью о том, как генерировать фиктивные данные при помощи 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'
В каких данные чаще всего возникает необходимость в вашей работе? Что в библиотеке упустили и что необходимо немедленно добавить? Мы были бы очень рады услышать ваши пожелания/рекомендации/замечания.
Ссылка на проект: тут.
На документацию ссылка: тут.
На первую часть статьи: тут.
На этом у меня все, друзья. Вам удачных тестов и да пребудет с вами сила!
Wayfarer15
Где-то тут должна быть ссылка на такое понятие как Data Masking, в особенности если данные используются для тестирования healthcare приложений. По этому поводу понаписано куча трудов, например — http://www.datamasker.com/DataMasking_WhatYouNeedToKnow.pdf
likid_ri
Спасибо за ссылку. Обязательно почитаю.