Skip to content

About

Python PostgreSQL Persistence Backend

Resources

Stars

10 stars

Watchers

10 watching

Forks

 
 

Repository files navigation

pjpersist

https://coveralls.io/repos/github/Shoobx/pjpersist/badge.svg?branch=master Maintainability

A Python PostgreSQL/JSONB Persistence Backend.

Providing transparent persistence of Python objects.

This document outlines the general capabilities of the pjpersist package. pjpersist is a PostgreSQL/JSONB storage implementation for persistent Python objects. It is NOT a storage for the ZODB.

The goal of pjpersist is to provide a data manager that serializes objects to PostgreSQL using JSONB at transaction boundaries. The PJ data manager is a persistent data manager, which handles events at transaction boundaries (see transaction.interfaces.IDataManager) as well as events from the persistency framework (see persistent.interfaces.IPersistentDataManager).

An instance of a data manager is supposed to have the same life time as the transaction, meaning that it is assumed that you create a new data manager when creating a new transaction:

>>> import transaction

Let's now define a simple persistent object:

>>> import persistent
>>> class Person(ReprMixin, persistent.Persistent):
...
...     def __init__(self, name, phone=None, address=None, friends=None,
...                  visited=(), birthday=None):
...         self.name = name
...         self.address = address
...         self.friends = friends or {}
...         self.visited = visited
...         self.phone = phone
...         self.birthday = birthday
...         self.today = datetime.datetime(2014, 12, 4, 12, 30, 0)
...
...     def __str__(self):
...         return self.name

Let's create a new person and store it in PostgreSQL:

>>> stephan = Person(u'Stephan')
>>> dm.root['stephan'] = stephan

By default, persistent objects are stored in a tabke having the Python path of the class. Since table names cannot statewith an underscore and contain dots, we have to escpae the path a little bit. Let's see what got stored in PostgreSQL:

>>> dumpTable('u__main___dot_Person')
[{'data': {u'_py_persistent_type': u'__main__.Person',
           u'address': None,
           u'birthday': None,
           u'friends': {},
           u'name': u'Stephan',
           u'phone': None,
           u'today': {u'_py_type': u'datetime.datetime',
                      u'value': u'2014-12-04T12:30:00.000000'},
           u'visited': []},
  'id': u'0001020304050607080a0b0c0'}]

Let's now add an address for Stephan. Addresses are also persistent objects:

>>> class Address(ReprMixin, persistent.Persistent):
...     _p_pj_table = 'address'
...
...     def __init__(self, city, zip):
...         self.city = city
...         self.zip = zip
...
...     def __str__(self):
...         return '%s (%s)' %(self.city, self.zip)
>>> stephan.address = Address('Maynard', '01754')

We need to commit the transaction, to push the data to PostgreSQL:

>>> transaction.commit()
>>> dumpTable('address')
[{'data': {u'_py_persistent_type': u'__main__.Address',
           u'city': u'Maynard',
           u'zip': u'01754'},
  'id': u'0001020304050607080a0b0c0'}]

As you can see, even the reference to the Address object looks nice and uses the standard PostgreSQL reference construct.

>>> dumpTable('u__main___dot_Person')
[{'data': {u'_py_persistent_type': u'__main__.Person',
           u'address': {u'_py_type': u'DBREF',
                        u'database': u'pjpersist_test',
                        u'id': u'0001020304050607080a0b0c0',
                        u'table': u'address'},
           u'birthday': None,
           u'friends': {},
           u'name': u'Stephan',
           u'phone': None,
           u'today': {u'_py_type': u'datetime.datetime',
                      u'value': u'2014-12-04T12:30:00.000000'},
           u'visited': []},
  'id': u'0001020304050607080a0b0c0'}]

But what about arbitrary non-persistent, but picklable, objects? Well, let's create a phone number object for that:

>>> class Phone(ReprMixin):
...
...     def __init__(self, country, area, number):
...         self.country = country
...         self.area = area
...         self.number = number
...
...     def __str__(self):
...         return '%s-%s-%s' %(self.country, self.area, self.number)
>>> stephan = dm.root['stephan']
>>> stephan.phone = Phone('+1', '978', '394-5124')
>>> transaction.commit()
>>> dumpTable('u__main___dot_Person')
[{'data': {u'_py_persistent_type': u'__main__.Person',
           u'address': {u'_py_type': u'DBREF',
                        u'database': u'pjpersist_test',
                        u'id': u'0001020304050607080a0b0c0',
                        u'table': u'address'},
           u'birthday': None,
           u'friends': {},
           u'name': u'Stephan',
           u'phone': {u'_py_type': u'__main__.Phone',
                      u'area': u'978',
                      u'country': u'+1',
                      u'number': u'394-5124'},
           u'today': {u'_py_type': u'datetime.datetime',
                      u'value': u'2014-12-04T12:30:00.000000'},
           u'visited': []},
  'id': u'0001020304050607080a0b0c0'}]

Let's now set various attributes:

>>> stephan = dm.root['stephan']
>>> stephan.friends = {'roy': Person(u'Roy Mathew')}
>>> stephan.visited = (u'Germany', u'USA')
>>> stephan.birthday = datetime.date(1980, 1, 25)

Push the data to PostgreSQL, and dump the results:

>>> transaction.commit()
>>> dumpTable('u__main___dot_Person')
[{'data': {u'_py_persistent_type': u'__main__.Person',
           u'address': {u'_py_type': u'DBREF',
                        u'database': u'pjpersist_test',
                        u'id': u'0001020304050607080a0b0c0',
                        u'table': u'address'},
           u'birthday': {u'_py_type': u'datetime.date',
                         u'value': u'1980-01-25'},
           u'friends': {u'roy': {u'_py_type': u'DBREF',
                                 u'database': u'pjpersist_test',
                                 u'id': u'0001020304050607080a0b0c0',
                                 u'table': u'u__main___dot_Person'}},
           u'name': u'Stephan',
           u'phone': {u'_py_type': u'__main__.Phone',
                      u'area': u'978',
                      u'country': u'+1',
                      u'number': u'394-5124'},
           u'today': {u'_py_type': u'datetime.datetime',
                      u'value': u'2014-12-04T12:30:00.000000'},
           u'visited': [u'Germany', u'USA']},
  'id': u'0001020304050607080a0b0c0'},
 {'data': {u'_py_persistent_type': u'__main__.Person',
           u'address': None,
           u'birthday': None,
           u'friends': {},
           u'name': u'Roy Mathew',
           u'phone': None,
           u'today': {u'_py_type': u'datetime.datetime',
                      u'value': u'2014-12-04T12:30:00.000000'},
           u'visited': []},
  'id': u'0001020304050607080a0b0c0'}]

Of course all properties can be retrieved as python objects:

>>> stephan = dm.root['stephan']
>>> stephan.address
<Address Maynard (01754)>
>>> stephan.address.city
u'Maynard'
>>> stephan.birthday
datetime.date(1980, 1, 25)
>>> stephan.friends
{u'roy': <Person Roy Mathew>}
>>> stephan.phone
<Phone +1-978-394-5124>
>>> stephan.today
datetime.datetime(2014, 12, 4, 12, 30)
>>> stephan.visited
[u'Germany', u'USA']

See src/pjpersist/README.txt and the other txt files in the package for more details.

About

Python PostgreSQL Persistence Backend

Resources

Stars

10 stars

Watchers

10 watching

Forks

Releases

Packages

Used by

Contributors

Languages