YAML для создания тестовых данных
Сегодня в качестве эксперимента я пощупал формат описания данных YAML и успешно заюзал его для наполнения проекта тестовыми данными. В начале я хотел просто хранить множество данных в YAML-файле, затем скриптом доставать известные заранее ключи и записывать их в нужные модели, но затем как всегда душе захотелось большего и я решил хранить в YAML также информацию о названиях моделей и полей. То есть теперь одним скриптом я могу парсить разные YAML-файлы.
Конечно, хотелось бы это всё красочно расписать, да больно неохото палец сосать, так что выкладываю код и баста!
Вот это YAML-файл, по которому генерятся данные для различных моделей. Обратите внимание, это не просто модели в себе, они связаны друг с другом отношениями.
А вот собственно скрипт для парсинга:
Ну как, это какой-то велосипед аццкий или здравое зерно всё таки есть? :-)
UPD: Подправил скрипт, начальная версия оказывается неправильно обрабатывала ForeignKey. Теперь правильно + умеет many to many отношения )
Конечно, хотелось бы это всё красочно расписать, да больно неохото палец сосать, так что выкладываю код и баста!
Вот это YAML-файл, по которому генерятся данные для различных моделей. Обратите внимание, это не просто модели в себе, они связаны друг с другом отношениями.
meta:
order: [Agency, Client, User, Compaign]
defaults:
User:
is_active: True
Agency:
- name: Суперстар Агенство
ref: star
- name: Unlimited Success
ref: success
User:
# Agency managers
- username: mars
agency: {Agency: star}
is_staff: True
is_superuser: True
- username: boss
agency: {Agency: success}
is_staff: True
is_superuser: True
# Client managers
- username: balt_manager
client: {Client: baltika}
- username: blat_manager
client: {Client: gazprom}
Client:
- name: Балтика
ref: baltika
- name: Газпром
ref: gazprom
Compaign:
- name: Пей, пока жив
client: {Client: baltika}
- name: Наше пиво не токсично
client: {Client: baltika}
- name: А у нас на кухне газ
client: {Client: gazprom}
А вот собственно скрипт для парсинга:
#!/usr/bin/env python
"""
This module fill project database with objects described in YAML file.
"""
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import yaml
import logging
from itertools import chain
from agency.models import Agency
from django.contrib.auth.models import User
from task.models import Compaign
from client.models import Client
def parse():
conf = yaml.load(open('init.ya').read())
meta = conf.pop('meta')
objects = {}
for model_name in meta['order']:
model = globals()[model_name]
logging.debug('Deleting all %s instances' % model_name)
model.objects.all().delete()
defaults = meta['defaults'].get(model_name, {})
for item in conf[model_name]:
if 'ref' in item:
ref = item.pop('ref')
else:
ref = None
obj = model()
m2m = {}
for key, value in chain(item.iteritems(), defaults.iteritems()):
if isinstance(value, dict):
fk_model, fk_ref = value.items()[0]
setattr(obj, key, objects[fk_model][fk_ref])
elif isinstance(value, (dict, list)):
for m2m_value in value:
fk_model, fk_ref = m2m_value.items()[0]
m2m.setdefault(key, []).append(objects[fk_model][fk_ref])
else:
setattr(obj, key, value)
obj.save()
for key, values in m2m.iteritems():
setattr(obj, key, values)
logging.debug(u'New %s instance: %s' % (model_name, obj))
if ref:
objects.setdefault(model_name, {})[ref] = obj
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
parse()
Ну как, это какой-то велосипед аццкий или здравое зерно всё таки есть? :-)
UPD: Подправил скрипт, начальная версия оказывается неправильно обрабатывала ForeignKey. Теперь правильно + умеет many to many отношения )



















Comments
да, это лучше выглядит по сравнению со стандартной django-вской сериализацией в YAML и обратно. понравилось использование ref'ов. для ручками заданных тестовых данных это плюс. зачем в meta нужен order, я не понял.
Потому что мне было лень писать логику вычисляющую этот порядок: в начале надо создать объекты, от которые зависят другие объекты. Я решил это в лоб сделать, хотя, конечно, это не совсем красиво и по хорошему надо переписать )
А обратная задача, из моделей вылить вот в такой вот YAML? Чтобы тестовые данные можно было просто в админке и создавать.
Django'вская сериализация хороша тем что работает в обе стороны. А JSON не так уж ужасен.
Ну, обратная задача вполне решается средствами ./manage.py: dumpdata, а затем loaddata.