The ZODB uses various Python hooks to catch attribute accesses, and
can trap most of the ways of modifying an object, but not all of them.
If you modify a User object by assigning to one of its
attributes, as in userobj.first_name = 'Andrew'
, the ZODB will
mark the object as having been changed, and it'll be written out on
the following commit().
The most common idiom that isn't caught by the ZODB is
mutating a list or dictionary. If User objects have a
attribute named friends
containing a list, calling
userobj.friends.append(otherUser)
doesn't mark
userobj
as modified; from the ZODB's point of
view, userobj.friends
was only read, and its value, which
happened to be an ordinary Python list, was returned. The ZODB isn't
aware that the object returned was subsequently modified.
This is one of the few quirks you'll have to remember when using the ZODB; if you modify a mutable attribute of an object in place, you have to manually mark the object as having been modified by setting its dirty bit to true. This is done by setting the _p_changed attribute of the object to true:
userobj.friends.append(otherUser) userobj._p_changed = 1
An obsolete way of doing this that's still supported is calling the __changed__() method instead, but setting _p_changed is the preferred way.
You can hide the implementation detail of having to mark objects as
dirty by designing your class's API to not use direct attribute
access; instead, you can use the Java-style approach of accessor
methods for everything, and then set the dirty bit within the accessor
method. For example, you might forbid accessing the friends
attribute directly, and add a get_friend_list() accessor and
an add_friend() modifier method to the class. add_friend()
would then look like this:
def add_friend(self, friend): self.friends.append(otherUser) self._p_changed = 1
Alternatively, you could use a ZODB-aware list or mapping type that handles the dirty bit for you. The ZODB comes with a PersistentMapping class, and I've contributed a PersistentList class that's included in my ZODB distribution, and may make it into a future upstream release of Zope.