Re: [Zope3-Users] Re: Evolution of ZoDB after changing python modules structure (moving content classes to another module)

2007-06-12 Thread Rupert Redington
Aleksander Kowalczyk wrote:
 On 6/8/07, Alek Kowalczyk [EMAIL PROTECTED] wrote:

 Alek Kowalczyk [EMAIL PROTECTED] writes:

 
  Hi,
  I moved my content class from mypackage.mymodule.MyContentClass into
  mypackage.mysubpackage.mymodule.MyContentClass.
  But when started Zope and went to visit an object I have previously
 created (of
  MyContentClass), I get:
  ComponentLookupError: ((persistent broken
 mypackage.mymodule.MyContentClass
  instance '\x00\x00\x00\x00\x00\x00\x02q',
  zope.publisher.browser.BrowserRequest instance

 I found a quite well working solution on
 http://mail.zope.org/pipermail/zodb-dev/2006-September/010382.html

 There was only one issue: this solution assumes that we have given a DB,
 while
 Zope evolve method receives already open connection. Unfortunately
 classFactory
 from DB is cached in Connection's private fields in constructor.
 Because of that I could not assign my custom 'renaming' class factory
 using:

 def evolve(context): #won't work!
 context.connection.db().classFactory = myClassFactory

 Instead I had to do some dirty private fields substitution in
 Connection's
 ObjectReader:

 def evolve(context): #this works nice
 context.connection._reader._factory = myClassFactory
 
 
 I shouldn't announce success too early. The solution works but only during
 first Zope run (i.e just after evolving the schema).
 Although classFactory returns proper class during evolve, the new class
 name
 is not saved in ZoDB, so after next unghosting object get old class names
 again.
 
 Here is my evolve script. I really don't know what more should I do to make
 Zope/ZoDB write the new class name in ZoDB. Can someone help me a bit... :)
 ?
 
 def convertingClassFactory(connection, moduleName, globalName):
#convert class name to new one and return the class object
 
 def evolve(context):
#dirty hack to substitute classFactory
context.connection._reader._factory = convertingClassFactory
root = context.connection.root().get(ZopePublication.root_name, None)
for object in findObjectsMatching(root, lambda x: True):
if hasattr(object, '_p_activate'):
object._p_activate()
object._p_changed = True
 

Somebody correct me if I'm wrong
I imagine you'd need to commit the transaction manually:


 def evolve(context):
#dirty hack to substitute classFactory
context.connection._reader._factory = convertingClassFactory
root = context.connection.root().get(ZopePublication.root_name, None)
for object in findObjectsMatching(root, lambda x: True):
 if hasattr(object, '_p_activate'):
object._p_activate()
object._p_changed = True
import transaction
transaction.commit()


Hope that helps,

Rupert
___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users


Re: [Zope3-Users] Re: Evolution of ZoDB after changing python modules structure (moving content classes to another module)

2007-06-12 Thread Marius Gedminas
On Sun, Jun 10, 2007 at 09:14:24AM +0200, Aleksander Kowalczyk wrote:
 On 6/8/07, Alek Kowalczyk [EMAIL PROTECTED] wrote:
 
 Alek Kowalczyk [EMAIL PROTECTED] writes:
 
 
  Hi,
  I moved my content class from mypackage.mymodule.MyContentClass into
  mypackage.mysubpackage.mymodule.MyContentClass.
  But when started Zope and went to visit an object I have previously
 created (of
  MyContentClass), I get:
  ComponentLookupError: ((persistent broken
 mypackage.mymodule.MyContentClass
  instance '\x00\x00\x00\x00\x00\x00\x02q',
  zope.publisher.browser.BrowserRequest instance
 
 I found a quite well working solution on
 http://mail.zope.org/pipermail/zodb-dev/2006-September/010382.html
 
 There was only one issue: this solution assumes that we have given a DB,
 while
 Zope evolve method receives already open connection. Unfortunately
 classFactory
 from DB is cached in Connection's private fields in constructor.
 Because of that I could not assign my custom 'renaming' class factory
 using:
 
 def evolve(context): #won't work!
 context.connection.db().classFactory = myClassFactory
 
 Instead I had to do some dirty private fields substitution in Connection's
 ObjectReader:
 
 def evolve(context): #this works nice
 context.connection._reader._factory = myClassFactory
 
 
 I shouldn't announce success too early. The solution works but only during
 first Zope run (i.e just after evolving the schema).
 Although classFactory returns proper class during evolve, the new class name
 is not saved in ZoDB, so after next unghosting object get old class names
 again.
 
 Here is my evolve script. I really don't know what more should I do to make
 Zope/ZoDB write the new class name in ZoDB. Can someone help me a bit... :)
 ?
 
 def convertingClassFactory(connection, moduleName, globalName):
#convert class name to new one and return the class object
 
 def evolve(context):
#dirty hack to substitute classFactory
context.connection._reader._factory = convertingClassFactory
root = context.connection.root().get(ZopePublication.root_name, None)
for object in findObjectsMatching(root, lambda x: True):
if hasattr(object, '_p_activate'):
object._p_activate()
object._p_changed = True

Note that findObjectMatching will not return all the persistent objects,
but only those that are directly placed in containers.  Other objects
(e.g. ones stored in annotations) refer to your old classes, you'll need
to do more.  Stephan Richter once figured out how to do that for
SchoolTool, IIRC it involved looping through all OIDs in the database
and marking them as _p_changed.

Marius Gedminas
-- 
Whom the gods would destroy, they first teach BASIC.


signature.asc
Description: Digital signature
___
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users