[Zope] persistence and dictionaries

Matt matt.bion@eudoramail.com
Fri, 08 Dec 2000 21:00:06 +1300


Thanks for the reply, that is really useful.  There are a couple of things
though that still don't add up.  Firstly, you say below, as do all the ZODB
documents that "Custom" classes can certainly persist, they just need to mix in
the "Persistence.Persistent" class as a base class.  Well, in my example I
attached in my first email, my product certainly has Persistence.Persistent,
but my second class that I add to this one does not, yet it still persists.
There was an email sometime ago on the mailing list that told someone that this
was why their product instances disappearing from the ZODB.
(the ref for the original email is : http://www.egroups.com/message/zope/44263
... I can't find the reply again.)

So my current understanding would be that any classes you want to add in do not
need to derive from Persistence.Persistent, and if it is pickleable then all
should be fine if you call on instances of that object within you product.

The next part that worried me came from the "python product tutorial"
http://www.zope.org/Members/hathawsh/PythonProductTutorial

This stated that the class dictionary self.votes = {} needed to be changed to
self._votes = Globals.PersistentMapping()  so that updates to it persist.
Hence my query about dictionaries.

I also noticed your comment about __setstate__ .  What is it about this that is
dangerous.  Recently I built a product out of some python classes I wrapped
around 4DOM, and since 4DOM documents do not seem to persist(well the document
does, but it loses all its children), then I persisted them to the local file
system, since I needed to do that anyway for what I was doing.  Setstate seemed
to work nicely to bring them back, though watching its behaviour I noticed that
it was called very often by zope.

regards
Matt


Chris McDonough wrote:

> All pickleable Python primitive types (strings, dictionaries, lists, Nones,
> integers, floats, longs, etc.) can live in the ZODB.  They can persist just
> like instances that inherit from the Persistent class.
>
> I think you read a little too much in to the fact that you need to "treat
> mutable objects immutably" when working with them in the ZODB.  This
> statement doesn't mean that these kinds of objects can't be saved in the
> ZODB, it just means you need to treat them specially when putting them in
> the database.
>
> For instance, if you were doing this inside of an external method:
>
> def amethod(self):
>    self.mydict = {}
>    self.mydict['a'] = 1
>
> (where self is the persistent object that is usually the external method's
> "container")
>
> It wouldn't work as you expected.  Although you'd see an 'a' in mydict for a
> little while in further accesses to it, 'mydict' would eventaully show up as
> an empty dictionary on the first access of it after it was expired from the
> RAM cache (after it was 'ghosted'), because the last thing that the ZODB
> "saw" (via the __setattr__ on 'self' and a subsequent transaction) was you
> setting a empty dictionary.
>
> Persistent objects (like "self" in the above example) are only smart enough
> to notice changes to themselves that happen through their __setattr__ (e.g.
> self.mydict = {} calls self's __setattr__).  Mutating the attribute 'mydict'
> above "in-place" (via self.mydict['a'] = 1) does not trigger self's
> __setattr__, so the ZODB never notices that "mydict" got changed.
>
> There are two ways to handle this.  The first is to treat mutable attributes
> "immutably" via assigning to a temporary variable and then making sure the
> persistent container's __setattr__ gets called:
>
> def amethod(self):
>    dict = {}
>    dict['a'] = 1
>    self.mydict = dict # trigger persistence mechanism implicitly
>
> The second is to use the _p_changed attribute of the persistent object on
> which the primitive is set.  This explcitly tells the persistence system to
> include the object on which it's set into the current transaction:
>
> def amethod(self):
>    self.mydict = {}
>    self.mydict['a'] = 1
>    self._p_changed = 1 # trigger persistence mechanism manually
>
> Variations on this theme extend to list methods too (e.g. list.append,
> list.pop, etc.)
>
> "Custom" classes can certainly persist, they just need to mix in the
> "Persistence.Persistent" class as a base class.
>
> As long as you obey this (slightly annoying, but necessary) rule, you
> shouldn't need to use PersistentMapping (it's really just a convenient
> wrapper that does this "magic" for you) or make any other wrappers for other
> mutable objects.
>
> I don't know why you're using __setstate__, but ummmm.. I won't go there.
> :-)  I didn't look at your product, but I don't think I need to to answer
> your question...
>
> > Hi I am trying to get a handle on how I should handle peristence in my
> > python products.  I have read all the ZODB documents and all the Product
> > tutorials, which all led me to believe that 1) mutable objects such as
> > lists and dictionaries are not persistent and that updates to these will
> > not be implicitly saved into the ZODB, and 2) that custom classes would
> > certainly not persist.  That all seemed fine, and I used persitent
> > mapping and __setstate__ to fill things back in where necessary.  But
> > then I decided to demonstrate that persitence does break as suggested.
> >
> > I used boring product, added a dicitonary and a custom class that
> > contains it's own dictionary.... let the user update name : value pairs
> > for both, and print the contents through index.dtml
> >
> > The problem is that everything persists fine !!!!  through restarts,
> > everything?
> >
> > Why does it work???  shouldn't it not?
> >
> > I have attached the modified boring product .... boringplus ..... it's
> > dead simple to follow if you have made products before.
> >
> > Any explanation would be really nice.
> >
> > regards
> > Matt
> >
> >