In Plone, all content is mapped to a single tree: content objects, user objects, templates, etc. Even almost all object methods are directly mapped to HTTP visible URIs.
Each object has a path depending on its location. Traversing is a method of getting a handle of a persistent object in ZODB object graph by its path.
Traversing can happen in two places
Each content object has an id string which identifies the object in the parent container. The id string is visible in the browser address bar when you view the object. Ids are also visible in the Zope Management interface.
Besides id strings, the content objects have Unique Identifiers, or UID, which does not change even if the object is moved or renamed.
Id should not contain spaces or slashes.
The Zope path is the location of the object in the object graph. It is a sequence of id components from the parent node(s) to the child separated by slashes.
Example:
documentation/howTos/myHowTo
You can use the Zope Management interface to explore the content of your Zope application server:
The ZMI does not expose individual attributes. It only exposes traversable content objects.
Zope exposes child objects as attributes.
Example:
# you have obtained the plone.org portal root object somehow and it's
# stored in local variable "portal"
documentation = portal.documentation
howTos = getattr(portal, "how-to") # note that we need use getattr because dash is invalid in syntax
myHowTo = getattr(howTos, "manipulating-plone-objects-programmatically")
Zope exposes child objects as container accessor.
Example:
# you have obtained the plone.org portal root object somehow and it's
# stored in a local variable "portal"
documentation = portal["documentation"]
howTos = documentation["how-to"]
myHowTo = howTos["manipulating-plone-objects-programmatically"]
Any content object provides the methods restrictedTraverse() and unrestrictedTraverse(). See Traversable.
Security warning: restrictedTraverse() uses privileges of the currently logged in user. An Unauthorized exception is raised if the code tries to access an object for which the user lacks Access contents information and View permissions.
Example:
myHowTo = portal.restrictedTraverse("documentation/howTos/myHowTo")
# Bypass security
myHowTo = portal.unrestrictedTraverse("documentation/howTos/myHowTo")
警告
restrictedTraverse()/unrestrictedTraverse() does not honor IPublishTraverse adapters. Read more about the issue in this discussion.
An object has two paths:
Path mangling warning: Always store paths as virtual paths or persistently stored paths will corrupt if you rename your site instance.
See Traversable.
Use getPhysicalPath(). Example:
path = portal.getPhysicalPath() # returns "plone"
Map physical path to virtual path using HTTP request object physicalPathToVirtualPath(). Example:
request = self.request # HTTPRequest object
path = portal.document.getPhysicalPath()
virtual_path = request.physicalPathToVirtualPath(path) # returns "document"
ノート
Virtual path is not necessarily path relative to the site root, depending on the virtual host configuration.
There is no a direct, easy, way to accomplish this.
Example:
def getSiteRootRelativePath(context, request):
""" Get site root relative path to an item
@param context: Content item which path is resolved
@param request: HTTP request object
@return: Path to the context object, relative to site root, prefixed with a slash.
"""
portal_state = getMultiAdapter((context, request), name=u'plone_portal_state')
site = portal_state.portal()
# Both of these are tuples
site_path = site.getPhysicalPath();
context_path = context.getPhysicalPath()
relative_path = context_path[len(site_path)+1:]
return "/" + "/".join(relative_path)
Use absolute_url(). See Traversable.
URL mangling warning: absolute_url() is sensitive to virtual host URL mappings. absolute_url() will return different results depending on if you access your site from URLs http://yourhost/ or http://yourhost:8080/Plone. Do not persistently store the result of absolute_url().
Example:
url = portal.absolute_url() # http://nohost/plone in unit tests
Object parent is accessible is acquisition chain for the object is set.
Use aq_parent.
parent = object.aq_parent
Parent is defined as __parent__ attribute of the object instance.
object.__parent__ = object.aq_parent.
__parent__ is set when object’s __of__() method is called.
view = MyBrowserView(context, request)
view = view.__of__(context) # Inserts view into acquisition chain and acquistion functions become available
Example:
def getAcquisitionChain(object):
"""
@return: List of objects from context, its parents to the portal root
Example::
chain = getAcquisitionChain(self.context)
print "I will look up objects:" + str(list(chain))
@param object: Any content object
@return: Iterable of all parents from the direct parent to the site root
"""
# It is important to use inner to bootstrap the traverse,
# or otherwise we might get surprising parents
# E.g. the context of the view has the view as the parent
# unless inner is used
inner = object.aq_inner
iter = inner
while iter is not None:
yield iter
if ISiteRoot.providedBy(iter):
break
if not hasattr(iter, "aq_parent"):
raise RuntimeError("Parent traversing interrupted by object: " + str(parent))
iter = iter.aq_parent
You can resolve the site root if you have the handle to any context object.
Example:
from Products.CMFCore.utils import getToolByName
# you know some object which is refered as "context"
portal_url = getToolByName(context, "portal_url")
portal = portal_url.getPortalObject()
Site is also stored a thread local variable. In Zope each request is processed in its own thread. Site thread local is set when the request processing starts.
You can use this method even if you do not have the context object available, assuming that your code is called after Zope has traversed the context object once.
Example:
from zope.app.component.hooks import getSite
site = getSite() # returns portal root from thread local storage
You can also access other sites within the same application server from your code.
Example:
app = context.restrictedTraverse('/') # Zope application server root
site = app["plone"] # your plone instance
site2 = app["mysiteid"] # another site
Sometimes traversing can give you attributes which actually do not exist on the object, but are inherited from the parent objects in the persistent object graph. See acquisition.
You can create your own traversing hooks.
警告
If AttributeError is risen inside traverse() function bad things happen, as Zope publisher specially handles this and raises NotFound exception.
See object publishing.