Skip to content

Shoobx/pjpersist

This branch is 513 commits ahead of, 3 commits behind zopefoundation/mongopersist:refs/heads/master.

Folders and files

NameName
Last commit message
Last commit date
Jul 12, 2024
Mar 27, 2024
Feb 22, 2023
Feb 22, 2021
Mar 27, 2024
Nov 4, 2011
Jun 19, 2019
Mar 14, 2024
Jan 7, 2015
Mar 27, 2024
Feb 22, 2023

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

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 100.0%