ZODB, Zope Object Database, — это полнофункциональная, объектно-ориентированная база данных (OODB) специально для Python. ZODB можно рассматривать как более мощную альтернативу хранилищам модуля shelve, описываемым в предыдущем разделе. Она позволяет сохранять по ключу практически любые объекты Python подобно хранилищам shelve, но предлагает при этом ряд дополнительных особенностей в обмен на небольшое усложнение взаимодействия с ней.
ZODB — не единственная объектно-ориентированная база данных, доступная для программ на языке Python: система Durus в целом выглядит проще и во многом подобна ZODB. Однако, несмотря на некоторые преимущества, в настоящее время Durus не предлагает всех особенностей, имеющихся в ZODB, и используется не так широко (возможно просто потому, что является более новой). По этой причине концепции объектно-ориентированных баз данных будут рассматриваться в этом разделе на примере ZODB.
ZODB — это открытое, стороннее расширение для Python. Первоначально этот продукт разрабатывался как механизм базы данных для веб-сайтов в составе веб-фреймворка Zope, упоминавшегося в главе 12, но теперь он доступен в виде самостоятельного пакета. Его можно использовать во многих областях вне контекста Zope и Веб как универсальную систему управления базами данных.
ZODB не поддерживает язык запросов SQL, однако объекты, хранимые в этой базе данных, могут использовать всю мощь языка Python. Кроме того, в некоторых приложениях хранимые данные более естественно представлять в виде структурированных объектов Python. Реляционные системы, основанные на таблицах, часто вынуждены представлять такие данные в виде отдельных фрагментов, разбросанных по нескольким таблицам и связанных сложными, порой весьма медлительными в обработке соединениями по ключу, или как-то иначе отображать их в модель классов языка Python. Поскольку объектно-ориентированные базы данных сохраняют объекты Python непосредственно, они часто способны обеспечить более простую модель в виде систем, не требующих использования мощного языка SQL.
Принцип работы с базой данных ZODB очень близко напоминает работу с хранилищами, создаваемыми стандартным модулем shelve, описанными в предыдущем разделе. Так же как и модуль shelve, для реализации постоянно хранимых словарей с хранимыми объектами Python ZODB использует систему сериализации Python. Фактически база данных почти не имеет специализированного интерфейса — объекты сохраняются в ней за счет простого присваивания по ключу объекту корневого словаря ZODB или за счет встраивания их в объекты, хранящиеся в корне базы данных. И, как и в случае с модулем shelve, «записи» имеют вид обычных объектов Python, обрабатываемых с помощью обычного синтаксиса и инструментов Python.
Однако, в отличие от модуля shelve, ZODB добавляет особенности, имеющие большое значение для некоторых программ:
Возможность одновременного обновления
Вам не придется вручную блокировать доступ к файлам, чтобы избежать повреждения данных при наличии нескольких пишущих процессов, что необходимо при использовании модуля shelve.
Подтверждение и отмена транзакций
При аварийном завершении программы изменения не будут сохранены в базе данных, если они не были явно подтверждены.
Ав тома ти че ское сохра не ние изме не ний для не ко торых ти пов объ ектов в памяти
Объекты в ZODB, порожденные от суперкласса постоянно хранимых объектов, достаточно сообразительны, чтобы знать, когда можно обновить содержимое в базе данных при присваивании значений их атрибутам.
Ав тома ти че ское кэ широ ва ние объ ек тов
Для большей эффективности объекты кэшируются в памяти и автоматически удаляются из кэша, когда надобность в них отпадает.
Плат формо не за ви си мое хра ни ли ще
Все данные ZODB хранит в одном плоском файле и поддерживает файлы большого размера, поэтому она не подвержена ограничениям на размер файлов и проблемам из-за различий в форматах DBM, свойственных модулю shelve. Как мы видели ранее в этой главе, хранилище, созданное в Windows с использованием bsddb, может ока-
заться недоступным для сценариев, выполняющихся в Linux и использующих gdbm.
Благодаря этим преимуществам ZODB определенно заслуживает внимания, если вам потребуется постоянно сохранять объекты Python в базе данных при промышленной эксплуатации. Единственное, чем придется заплатить за использование ZODB, — это небольшой объем дополнительного программного кода:
• Для организации доступа к базе данных ZODB необходим некоторый объем типового программного кода — это не просто вызов функции открытия.
• Классы должны наследовать суперкласс постоянно хранимых объектов, если вы пожелаете воспользоваться преимуществом автоматического обновления при их изменении — классы постоянно хранимых объектов в целом не являются полностью независимыми от базы данных, как и при использовании модуля shelve, хотя это в принципе возможно.
Во многих приложениях эти недостатки с лихвой окупаются дополнительными возможностями, предоставляемыми системой ZODB сверх того, что дает модуль shelve.
Сильно сокращенный учебник по ZODB
К сожалению, когда я писал эти строки, в июне 2010 года, еще отсутствовала версия ZODB для Python 3.X. Вследствие этого примеры и описание версии для Python 2.X, присутствовавшие в предыдущем издании, были убраны из этого раздела. Однако из уважения к пользователям Python 2.X, а также к читателям, использующим Python 3.X и ждущим, когда материализуется версия ZODB 3.X, я добавил материалы и примеры из прошлого издания, касающиеся ZODB, в пакет примеров для этого издания.
Дополнительную информацию о пакете с примерами вы найдете в предисловии, и ищите информацию о ZODB в следующих каталогах:
C:\…\Dbase\Zodb-2.x # примеры использования ZODB из 3 издания
C:\…\Dbase\Zodb-2.x\Documentaion # Учебник по ZODB из 3 издания
Хотя я не умею предсказывать будущее, тем не менее ZODB для Python 3.X наверняка появится рано или поздно. Но пока ее нет, можно использовать другие объектно-ориентированные базы данных для Python 3.X, предлагающие некоторые дополнительные возможности.
Однако, чтобы дать вам некоторое представление о ZODB, мы кратко познакомимся с особенностями ее использования в Python 2.X. После установки поддержки ZODB в первую очередь необходимо создать базу данных:
…\PP4E\Dbase\Zodb-2.x> python
> >> from ZODB import FileStorage, DB
> >> storage = FileStorage.FileStorage(r’C:\temp\mydb.fs’)
> >> db = DB(storage)
> >> connection = db.open()
> >> root = connection.root()
Это практически стандартный, «типовой» программный код, выполняющий подключение к базе данных ZODB: здесь импортируются необходимые инструменты, создаются объекты классов FileStorage и DB, а затем открывается база данных и создается кор не вой объ ект. Корневым объектом является постоянно хранимый словарь, в котором сохраняются другие объекты. Объект класса FileStorage отображает базу данных в плоский файл. Имеются также интерфейсы к другим типам хранилищ, таким как хранилища на основе реляционной базы данных.
Добавление объектов в базу данных ZODB выполняется так же просто, как при использовании модуля shelve. Поддерживаются почти любые объекты Python, включая кортежи, списки, словари, экземпляры классов и их комбинации. Как при использовании модуля shelve, чтобы сделать объект постоянно хранимым, достаточно просто присвоить его по ключу корневому объекту базы данных:
> >> object1 = (1, ‘spam’, 4, ‘YOU’)
> >> object2 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
> >> object3 = {‘name’: [‘Bob’, ‘Doe’],
‘age’: 42,
‘job’: (‘dev’, ‘mgr’)}
> >> root[‘mystr’] = ‘spam’ * 3
> >> root[‘mytuple’] = object1
> >> root[‘mylist’] = object2
>>> root[‘mydict’] = object3
>>> root[‘mylist’]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Поскольку ZODB поддерживает возможность отмены транзакций, чтобы сохранить изменения в базе данных, их необходимо подтвердить. В конечном итоге объекты в сериализованном представлении сохраняются в файлы — здесь будут созданы три файла, включая файл с именем, которое было указано при открытии:
>>> import transaction
>>> transaction.commit()
>>> storage.close()
…\PP4E\Dbase\Zodb-2.x> dir /B c:\temp\mydb*
mydb.fs
mydb.fs.index
mydb.fs.tmp
Без заключительного подтверждения в этом сеансе ни одно из изменений не сохранилось бы. Именно такое поведение чаще всего бывает желательным — если программа завершится аварийно в середине процедуры, выполняющей изменения, уже выполненные к этому моменту частичные изменения не сохранятся. Фактически ZODB поддерживает обычную для баз данных операцию отмены.
Извлечение хранимых объектов из базы данных ZODB в другом сеансе или программе выполняется так же просто: нужно открыть базу данных, как было показано выше, и обратиться по индексам к корневому объекту, чтобы извлечь объекты в память. Подобно хранилищам, создаваемым модулем shelve, корневой объект базы данных поддерживает интерфейс словарей — к нему можно обращаться с указанием индексов, использовать методы словарей, определять количество записей и так далее:
…\PP4E\Dbase\Zodb-2.x> python
> >> from ZODB import FileStorage, DB
> >> storage = FileStorage.FileStorage(r’C:\temp\mydb.fs’)
> >> db = DB(storage)
> >> connection = db.open()
> >> root = connection.root() # соединиться
> >> len(root), root.keys() # размер, индекс
(4 [‘mylist’, ‘mystr’, ‘mytuple’, ‘mydict’])
> >> root[‘mylist’] # извлечь объекты
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> root[‘mydict’]
{‘job’: (‘dev’, ‘mgr’), ‘age’: 42, ‘name’: [‘Bob’, ‘Doe’]}
> >> root[‘mydict’][‘name’][-1] # Фамилия Боба
‘Doe’
Так как корневой объект базы данных выглядит как словарь, с ним можно работать как с обычным словарем, например выполнить итерации по списку ключей, чтобы обойти все записи:
> >> for key in root.keys():
print(‘%s => %s’ % (key.ljust(10), root[key]))
mylist => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
mystr => spamspamspam
mytuple => (1, ‘spam’, 4, ‘YOU’)
mydict => {‘job’: (‘dev’, ‘mgr’), ‘age’: 42, ‘name’: [‘Bob’, ‘Doe’]}
Кроме того, подобно модулям pickle и shelve ZODB поддерживает возможность сохранения и извлечения экземпляров классов, правда при этом они должны наследовать суперкласс Persistent, реализующий необходимый протокол и перехватывающий операции изменения атрибутов, чтобы обеспечить автоматическое сохранение их на диск:
from persistent import Persistent class Person(Persistent):
def __init__(self, name, job=None, rate=0):
self.name = name self.job = job self.rate = rate def changeRate(self, newrate): self.rate = newrate # автоматически обновит базу данных
При изменении экземпляров классов, хранимых в ZODB, операции изменения атрибутов в памяти приводят к автоматическому сохранению изменений в базе данных. Другие типы операций изменения, такие как добавление элементов в список и присваивание словарям по ключу, все еще требуют повторно выполнять присваивание по оригинальному ключу корневому объекту, чтобы обеспечить принудительное сохранение изменений на диск (встроенные словари и списки не знают, что они являются постоянно хранимыми).
Поскольку пока еще не вышла версия ZODB для Python 3.X, это все, что можно сказать об этой базе данных в этой книге. За дополнительной информацией обращайтесь к ресурсам Интернета, посвященным проектам ZODB и Zope, а также ознакомьтесь с ресурсами в пакете с примерами, о которых говорилось выше. А теперь посмотрим, какие инструменты доступны программам на языке Python для работы с совершенно иной разновидностью баз данных — с реляционными базами данных и SQL.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011