Data Persistence - ZODB



ZODB (Zope object Database) is database for storing Python objects. It is ACID compliant - feature not found in NOSQL databases. The ZODB is also open source, horizontally scalable and schema-free, like many NoSQL databases. However, it is not distributed and does not offer easy replication. It provides persistence mechanism for Python objects. It is a part of Zope Application server, but can also be independently used.

ZODB was created by Jim Fulton of Zope Corporation. It started as simple Persistent Object System. Its current version is 5.5.0 and is written completely in Python. using an extended version of Python's built-in object persistence (pickle).

Some of the main features of ZODB are −

  • transactions
  • history/undo
  • transparently pluggable storage
  • built-in caching
  • multiversion concurrency control (MVCC)
  • scalability across a network

The ZODB is a hierarchical database. There is a root object, initialized when a database is created. The root object is used like a Python dictionary and it can contain other objects (which can be dictionary-like themselves). To store an object in the database, it’s enough to assign it to a new key inside its container.

ZODB is useful for applications where data is hierarchical and there are likely to be more reads than writes. ZODB is an extension of pickle object. That's why it can be processed through Python script only.

To install latest version of ZODB let use pip utility −

pip install zodb

Following dependencies are also installed −

  • BTrees==4.6.1
  • cffi==1.13.2
  • persistent==4.5.1
  • pycparser==2.19
  • six==1.13.0
  • transaction==2.4.0

ZODB provides following storage options −

FileStorage

This is the default. Everything stored in one big Data.fs file, which is essentially a transaction log.

DirectoryStorage

This stores one file per object revision. In this case, it does not require the Data.fs.index to be rebuilt on an unclean shutdown.

RelStorage

This stores pickles in a relational database. PostgreSQL, MySQL and Oracle are supported.

To create ZODB database we need a storage, a database and finally a connection.

First step is to have storage object.

import ZODB, ZODB.FileStorage
storage = ZODB.FileStorage.FileStorage('mydata.fs')

DB class uses this storage object to obtain database object.

db = ZODB.DB(storage)

Pass None to DB constructor to create in-memory database.

Db=ZODB.DB(None)

Finally, we establish connection with the database.

conn=db.open()

The connection object then gives you access to the ‘root’ of the database with the ‘root()’ method. The ‘root’ object is the dictionary that holds all of your persistent objects.

root = conn.root()

For example, we add a list of students to the root object as follows −

root['students'] = ['Mary', 'Maya', 'Meet']

This change is not permanently saved in the database till we commit the transaction.

import transaction
transaction.commit()

To store object of a user defined class, the class must be inherited from persistent.Persistent parent class.

Advantages of Subclassing

Subclassing Persistent class has its advantages as follows −

  • The database will automatically track object changes made by setting attributes.

  • Data will be saved in its own database record.

  • You can save data that doesn’t subclass Persistent, but it will be stored in the database record of whatever persistent object references it. Non-persistent objects are owned by their containing persistent object and if multiple persistent objects refer to the same non-persistent subobject, they’ll get their own copies.

Let use define a student class subclassing Persistent class as under −

import persistent
   class student(persistent.Persistent):
   def __init__(self, name):
      self.name = name
   def __repr__(self):
      return str(self.name)

To add object of this class, let us first set up the connection as described above.

import ZODB, ZODB.FileStorage
storage = ZODB.FileStorage.FileStorage('studentdata.fs')
db = ZODB.DB(storage)
conn=db.open()
root = conn.root()

Declare object an add to root and then commit the transaction

s1=student("Akash")
root['s1']=s1
import transaction
transaction.commit()
conn.close()

List of all objects added to root can be retrieved as a view object with the help of items() method since root object is similar to built in dictionary.

print (root.items())
ItemsView({'s1': Akash})

To fetch attribute of specific object from root,

print (root['s1'].name)
Akash

The object can be easily updated. Since the ZODB API is a pure Python package, it doesn’t require any external SQL type language to be used.

root['s1'].name='Abhishek'
import transaction
transaction.commit()

The database will be updated instantly. Note that transaction class also defines abort() function which is similar to rollback() transaction control in SQL.

Advertisements