Originally published at
Pythy. You can comment here or
there.
По работе надо делать экспорт данных в
dbf. Зачастую спецификации к формату экспорта выглядит примерно так: описание собственно формата экспортируемого файла плюс некое количество справочников.
Под справочниками в данном контексте понимается таблица с двумя (редко больше) полями, первое поле - код (обычно называемое code или id, хотя некоторые умудряются называть его kod), второе - значение (val, value, name).
В
реляционных терминах это обыкновенная связь один-ко-многим. Однако нюанс заключается в том, что у меня в БД "кодировка" этих справочников иная. Плюс название справочных значений зачастую отличается. Благо, справочников исчислимое количество, так что вполне можно поручить человеку сделать перекодировку справочников, сразу же сопоставив справочные значения. Со временем количество перекодировочных значений растет, так что я решил все экспортные вещи выделить в отдельный модуль и не держать в основной БД. Встал вопрос о формате хранения справочников и перекодировок. Всякие
РСУБД (в том числе и такие легкие как
SQLite) не подходят: у них слишком большие накладные расходы. И дело не в скорости доступа, а в простоте кода. Требуется либо создавать дополнительный слой абстракции, чтобы не писать каждый раз идентичные SQL-запросы, либо использовать уже готовый
ORM (
SQLObject или
SQLAlchemy). Если же отказаться от мысли "SQL везде и всюду", то видны некоторые более подходящие варианты:
- Berkley DB. Хранит пары ключ-значение. И ключ и значение должны быть строками. Т.е. Python-объекты дополнительно нужно перед сохранением сериализировать (при помощи pickle). Можно и не утруждать себя - создатели Python уже позаботились об этом в модуле shelve. Однако в ZoPyRus я не раз слышал от Bogdan Maryniuk о том, что Berkley DB - ненадежное хранилище.
- twisted.persisted.dirdbm. Предоставляет dbm-like интерфейс, но хранилище - файловая система. Каждый ключ хранится как отдельный файл. Помимо этого, dirdbm не является thread-safe. Этот модуль не зависит от других частей Twisted, так что при желании его можно использовать отдельно. Имеет функционал, аналогичный shelve, позволяющий в качестве значений хранить не только строки, но и более сложные Python-объекты (естественно, не без ограничений).
- ZODB. Zope Object DB, объекто-ориентированная база данных, часть Zope. Хранилище-backend может быть произвольным (есть даже DemoStorage). По умолчанию - FileStorage, хранящий информацию в отдельном файле. Чем меня сразу привлек ZODB - так это ZEO (Zope Enterprise Objects). За "магическим" и ничего не говорящим словом enterprise стоит возможность доступа по сети к ZODB "из коробки". Ну и слово Zope не должно вводить в заблуждение - ZODB (в комплекте с ZEO) доступен как раздельный компонент, без установки всего Zope.
Так что мой выбор пал на ZODB. Для того, чтобы установить ZODB из исходников, нужен Python (не ниже 2.3, рекомендуется 2.4) и C-компилятор. Для Windows доступны бинарные (скомпилированные) модули. Правда, с бинарным модулем для Python-2.3 на Windows у меня возникла проблема:
Python 2.3.5 (#62, Feb 8 2005, 16:23:02) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from ZODB import FileStorage
Traceback (most recent call last):
File "", line 1, in ?
File "C:PythonLibsite-packagesZODBFileStorage__init__.py", line 3, in ?
from ZODB.FileStorage.FileStorage
File "C:PythonLibsite-packagesZODBFileStorageFileStorage.py", line 36, in ?
from ZODB.lock_file import LockFile
File "C:PythonLibsite-packagesZODBlock_file.py", line 39, in ?
_flags = fcntl.LOCK_EX | fcntl.LOCK_NB
AttributeError: 'module' object has no attribute 'LOCK_EX'
С Python-2.4 такого не возникло, так что я обновил на рабочей машине Python до 2.4.3 (тем более, давно собирался, да все никак повода не было). На Linux, ни с Python-2.3, ни с Python-2.4 проблем не было.
В Google нашел пару статей по ZODB, но ими лучше не пользоваться - они устарели, так что лучшей документацией будет та, что
идет в комплекте с ZODB.
На днях расскажу, как я реализовал эти самые справочники при помощи ZODB.