[Zope] persistence and dictionaries

Matt matt.bion@eudoramail.com
Sat, 09 Dec 2000 15:42:58 +1300


This is a multi-part message in MIME format.
--------------29FD94B21838971A22A1E8F6
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

um, nope, that's all there is to that class .... I'll attach  the whole product

Matt


Jonothan Farr wrote:

> Oops. You snipped the contents of MoreStuff.addStuff(). No way to tell what's
> going on without that!
> --jfarr
>
> ----- Original Message -----
> From: "Matt" <matt.bion@eudoramail.com>
> To: "Jonothan Farr" <jfarr@real.com>; <zope@zope.org>
> Sent: Friday, December 08, 2000 2:39 PM
> Subject: Re: [Zope] persistence and dictionaries
>
> > Ok, here are some of the offending bits out of my boringplus product
> > .... which I can send again if people want.  I have
> > another product that keeps a dictionary of cookies so I can see how many
> > times someone uses the back button to
> > access the same forms page again.  This worked as expected, where I had
> > to issue an _p_changed=1 to get them to
> > persist.  But I am doing a similar thing below, or so I thought, once
> > with a dictionary in the product and once in a
> > dictionary within a class that does not inherit Persistent.  This is
> > what is confusing.  Through restarts etc, everything
> > perisists
> >
> >
> > class Boring(
> >     OFS.SimpleItem.Item,   # A simple Principia object. Not Folderish.
> >     Persistent,            # Make us persistent. Yaah!
> >     Acquisition.Implicit,  # Uh, whatever.
> >     AccessControl.Role.RoleManager # Security manager.
> >     ):
> >
> >
> > def __init__(self, id, title=''):
> >      self._things = {}
> >      self._more = myClass.MoreStuff()
> >
> >
> > def manage_edit(self, title, REQUEST=None):
> >    if REQUEST is not None:
> >       if REQUEST.has_key('thing'):
> >          self._things[REQUEST['thing']] = REQUEST['thing_value']
> >   self.addToMore(REQUEST['thing'],REQUEST['thing_value'])
> >
> >
> > def addToMore(self,name,value):
> >         self._more.addStuff(name,value)
> >         return ""
> >
> >
> > class MoreStuff:
> >
> >     def __init__(self):
> >         self._stuff = {'animal':'monkey'}
> >
> >     def addStuff(self,stuff_name,stuff_item):
> >
> >
> > On Sat, 09 Dec 2000, Jonothan Farr wrote:
> > > Maybe I'm mistaken, but it seems like you can put an instance of an
> > object that
> > > doesn't inherit from Persistent into the ZODB just fine, but its
> > contents won't
> > > persist, so you'll always end up with a copy of the object as it was
> > first added
> > > to the database.
> > >
> > > --jfarr
> > >
> > > ----- Original Message -----
> > > From: "Chris McDonough" <chrism@digicool.com>
> > > To: <matt.bion@eudoramail.com>
> > > Cc: <zope@zope.org>
> > > Sent: Friday, December 08, 2000 11:57 AM
> > > Subject: Re: [Zope] persistence and dictionaries
> > >
> > >
> > > > Huh.  If they do, it's by chance only.  I'd be hard-pressed to
> > explain it.
> > > >
> > > > Do they inherit from *anything*?
> > > >
> > > > ----- Original Message -----
> > > > From: "Matt" <matt.bion@eudoramail.com>
> > > > To: "Chris McDonough" <chrism@digicool.com>
> > > > Cc: <zope@zope.org>
> > > > Sent: Friday, December 08, 2000 2:30 PM
> > > > Subject: Re: [Zope] persistence and dictionaries
> > > >
> > > >
> > > > > Chris, this was my original confusion .... the two places below
> > where you
> > > > say
> > > > >
> > > > > "You can put instances which do not inherit from
> > Persistence.Persistent in
> > > > > your database.  They just won't "stick".  They'll hang around
> > until the
> > > > > server is restarted or for an undetermined amount of time during
> > normal
> > > > > operations."
> > > > >
> > > > > "No.  It'll work for "a while" or until the server is restarted.
> > :-)"
> > > > >
> > > > > actually do persist after restarts ... that's what confused me,
> > they
> > > > wouldn't
> > > > > go away and they should!!
> > > > >
> > > > > regards
> > > > > Matt
> > > > >
> > > > > Chris McDonough wrote:
> > > > >
> > > > > > > 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.)
> > > > > > >
> > > > > >
> > > > > > You can put instances which do not inherit from
> > Persistence.Persistent
> > > > in
> > > > > > your database.  They just won't "stick".  They'll hang around
> > until the
> > > > > > server is restarted or for an undetermined amount of time during
> > normal
> > > > > > operations.
> > > > > >
> > > > > > > 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.
> > > > > >
> > > > > > No.  It'll work for "a while" or until the server is restarted.
> > :-)
> > > > > >
> > > > > > > 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.
> > > > > >
> > > > > > This was for convenience, I'd imagine.
> > > > > >
> > > > > > >  I also noticed your comment about __setstate__ .  What is it
> > about
> > > > this
> > > > > > that is
> > > > > > > dangerous.
> > > > > >
> > > > > > Nothing implicitly dangerous, but it can get confusing if you
> > have
> > > > multiple
> > > > > > revisions of your product and you use variables caused by
> > __setstate__.
> > > > > > Also, once you add a __setstate__ which modifies the object
> > in-place,
> > > > > > there's a likelihood that it can never go away (you're can never
> > be sure
> > > > if
> > > > > > all instances have been updated).
> > > > > >
> > > > > > > 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.
> > > > > >
> > > > > > Sure, that works... although at that point you're creating your
> > own
> > > > object
> > > > > > database.  :-)
> > > > > >
> > > > > > >
> > > > > > > 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
> > > > > > > > >
> > > > > > > > >
> > > > > > >
> > > > > > >
> > > > >
> > > > >
> > > > > _______________________________________________
> > > > > Zope maillist  -  Zope@zope.org
> > > > > http://lists.zope.org/mailman/listinfo/zope
> > > > > **   No cross posts or HTML encoding!  **
> > > > > (Related lists -
> > > > >  http://lists.zope.org/mailman/listinfo/zope-announce
> > > > >  http://lists.zope.org/mailman/listinfo/zope-dev )
> > > > >
> > > > >
> > > >
> > > >
> > > > _______________________________________________
> > > > Zope maillist  -  Zope@zope.org
> > > > http://lists.zope.org/mailman/listinfo/zope
> > > > **   No cross posts or HTML encoding!  **
> > > > (Related lists -
> > > >  http://lists.zope.org/mailman/listinfo/zope-announce
> > > >  http://lists.zope.org/mailman/listinfo/zope-dev )
> > > >
> > >
> > >
> > > _______________________________________________
> > > Zope maillist  -  Zope@zope.org
> > > http://lists.zope.org/mailman/listinfo/zope
> > > **   No cross posts or HTML encoding!  **
> > > (Related lists -
> > >  http://lists.zope.org/mailman/listinfo/zope-announce
> > >  http://lists.zope.org/mailman/listinfo/zope-dev )
> >
> >
> > _______________________________________________
> > Zope maillist  -  Zope@zope.org
> > http://lists.zope.org/mailman/listinfo/zope
> > **   No cross posts or HTML encoding!  **
> > (Related lists -
> >  http://lists.zope.org/mailman/listinfo/zope-announce
> >  http://lists.zope.org/mailman/listinfo/zope-dev )
> >
>
> _______________________________________________
> Zope maillist  -  Zope@zope.org
> http://lists.zope.org/mailman/listinfo/zope
> **   No cross posts or HTML encoding!  **
> (Related lists -
>  http://lists.zope.org/mailman/listinfo/zope-announce
>  http://lists.zope.org/mailman/listinfo/zope-dev )

--------------29FD94B21838971A22A1E8F6
Content-Type: application/x-gzip;
 name="boringplus.tar.gz"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filename="boringplus.tar.gz"

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
AAAAAAAAAAAAAAAAAAAAAPBBeALgT3hxAKAAAA==
--------------29FD94B21838971A22A1E8F6--