Persistent Objects Base Class
The `persistent` library provides a base class, `Persistent`, for creating translucent persistent Python objects. These objects can track their their own 'dirty' state, indicating when they have been modified and need to be saved by an external persistence layer (e.g., ZODB). It is a fundamental component for building object persistence systems. The current version is 6.5, with major releases typically aligning with Python version support updates and addressing core behavior changes.
Warnings
- breaking Python 2.7, 3.4, 3.5, and 3.6 support was dropped in `persistent` version 5.0. Python < 3.8 support was dropped in version 6.0.
- breaking The `__dict__` attribute of `Persistent` objects no longer returns a `_PersistentDict` but a plain `dict`.
- gotcha The `_p_changed` attribute is only automatically set to `True` for direct attribute assignments (e.g., `obj.attr = value`). Changes to mutable objects *inside* a `Persistent` object (e.g., `obj.my_list.append(item)`, `obj.my_dict[key] = value`) will NOT automatically mark the parent object as dirty if not managed by an active persistence layer that proxies these mutable objects. This is a common source of data loss if not handled correctly.
- gotcha The `persistent` library provides the `Persistent` base class and change tracking mechanism, but it does NOT provide an object storage or database system itself. It is designed to be used in conjunction with a persistence layer like ZODB (Zope Object Database) to actually save and retrieve objects.
Install
-
pip install persistent
Imports
- Persistent
from persistent import Persistent
Quickstart
from persistent import Persistent
class MyPersistentObject(Persistent):
def __init__(self, name="Default"):
self.name = name
self.attributes = {} # A mutable dictionary
def update_name(self, new_name):
# Assigning to a direct attribute will mark the object dirty
self.name = new_name
def add_attribute(self, key, value):
# For objects managed by a persistence layer (e.g., ZODB),
# modifying this dict would also mark the parent dirty.
# Without a persistence layer, direct dict modification here
# does NOT automatically set _p_changed.
self.attributes[key] = value
def describe(self):
return f"Name: {self.name}, Attributes: {self.attributes}"
# Create an instance
my_obj = MyPersistentObject("Initial Name")
print(f"1. Initial state: {my_obj.describe()}, _p_changed: {my_obj._p_changed}")
# Modifying a direct attribute marks it dirty
my_obj.update_name("New Name 1")
print(f"2. After update_name: {my_obj.describe()}, _p_changed: {my_obj._p_changed}")
# Simulate 'saving' (a persistence layer would set _p_changed to None)
my_obj._p_changed = None
print(f"3. After 'saving': {my_obj.describe()}, _p_changed: {my_obj._p_changed}")
# Modifying the mutable dictionary directly (without a storage proxy)
# does NOT automatically set _p_changed, which is a common gotcha.
my_obj.attributes['version'] = 1
print(f"4. After modifying internal dict: {my_obj.describe()}, _p_changed: {my_obj._p_changed}")
# To ensure persistence layer detects changes in mutable objects,
# you often need to manually set _p_changed or reassign the attribute
# (e.g., obj.attributes = new_dict_or_copy) or rely on a proxy provided by the storage.
my_obj._p_changed = True
print(f"5. Manually set _p_changed after internal change: {my_obj.describe()}, _p_changed: {my_obj._p_changed}")