I have a couple of broken objects that I wish to loop through in python script. My use case is as follows: I have renamed my custom product from my.oldproduct
to my.newproduct
. This has caused the previous objects saved with my.oldproduct
to be broken and thus inaccessible. There is a workaround to this as detailed here: Updating broken objects
Now what I want to do is create a python script in the ZMI to loop though all the broken content, change/update them, and thus cause them to be saved using my.newproduct.
I've been unable get the old objects as they are not listed. See a sample of my python script to list all the content in the site, yet they still do not show:
from Products.CMFCore.utils import getToolByName
app = context.restrictedTraverse('/')
sm = app.plone.getSiteManager()
catalog = getToolByName(context, 'portal_catalog')
results = catalog.searchResults()
count = 0
for obj in results:
print obj.meta_type
count += 1
print str("Found " + str(count) + " matching objects")
return printed
How can I get the broken objects from my.oldproduct
to be listed?
You'll need to traverse your whole ZODB manually, I'm afraid. If these objects are content objects, you should be able to use the standard OFS methods:
from collections import deque
from datetime import datetime
import transaction
from zope.app.component.hooks import setSite
from Testing.makerequest import makerequest
from AccessControl.SecurityManagement import newSecurityManager
from my.newproduct.types import ArchetypesContentType
site_id = 'Plone' # adjust to match your Plone site object id.
admin_user = 'admin' # usually 'admin', probably won't need adjusting
app = makerequest(app)
site = app[site_id]
setSite(site)
user = app.acl_users.getUser(admin_user).__of__(site.acl_users)
newSecurityManager(None, user)
def treeWalker(root):
# stack holds (parent, id, obj) tuples
stack = deque([(None, None, root)])
while stack:
parent, id, next = stack.popleft()
try:
stack.extend((next, id, child) for id, child in next.objectItems())
except AttributeError:
# No objectItems method
pass
yield parent, id, next
count = 0
for parent, id, obj in treeWalker(site):
if isinstance(obj, ArchetypesContentType):
print 'Found content type object {} at {}'.format(id, '/'.join(object.getPhysicalPath()))
obj._p_changed = True # mark it as changed, force a commit
count += 1
if count % 100 == 0:
# flush changes so far to disk to minimize memory usage
transaction.savepoint(True)
print '{} - Processed {} items'.format(datetime.now(), count)
transaction.commit()
This assumes you already included the work-around you linked to; there is little point in trying to do the above with ZODB.broken.Broken
objects.
The above script acts as a bin/instance run
script, run it as such:
bin/instance run path/to/this/script.py
You are going to process everything in the site, a fairly hefty process that will involve a lot of cache churn and possibly a large commit with potential conflicts. You do not want to run this as a through-the-web script, really.