Plone中获取链的问题
Plone中获取链的问题
http://www.315ok.org/blogfolder/95
http://www.315ok.org/logo.png
Plone中获取链的问题
Plone中获取链的问题
As you may have guessed from the previous examples, containment hierarchies are linked to Zope 2 acquisition. Recall from Chapter 4 how acquisition allows attributes on objects to come from parents—normally through the containment hierarchy—in what is known as an acquisition chain. In fact, it is possible for an object to have more than one acquisition chain, for example if it is being explicitly wrapped into a new chain as we will see in a moment. This is why it is normally safer to ask for the innermost chain when you want to walk the containment hierarchy:
requested attribute.
If you need to temporarily turn off implicit acquisition, you can use the aq_explicit attribute, which is provided by Acquisition.Implict. We use the aq_base function to discard the acquisition wrapper and get the raw object when testing object reference equivalence. Without aq_base, we would be comparing the object references of the acquisition wrappers themselves, not the objects they wrap.
Methods such as absolute_url() and getPhysicalPath(), which rely on acquisition to construct a path, continue to work.
An object's acquisition chain is constructed dynamically, during traversal or inspection from code. If an attribute on an acquisition-aware object returns another acquisition-aware object, then the former object is the immediate acquisition chain parent of the returned object. The acquisition chain can be inspected using the aq_ chain function:
If the use_context() method uses the context variable in a way that relies on containment hierarchy acquisition, for example because it conducts an explicit security check or because it needs to acquire a skin layer template or script, it may encounter spurious AttributeErrors. Therefore, you will often see code like this:
In the following example, we instantiate a content object in memory, without connecting it to the containment hierarchy, and then wrap it in the acquisition context of the portal:
In page templates or actions, you will sometimes see expressions such as context/guitars/fender. This is using traversal relative to a particular object, employing a similar heuristic to the one described for URL traversal. In fact, path expressions in page templates can also traverse to elements inside dictionaries and lists. This does not apply to the restrictedTraverse()/unrestrictedTraverse() methods.
Acquisition is taken into account here too.
Path traversal can be invoked from Python code relative to a content object, using this syntax:
>>> from Acquisition import aq_inner
>>> containment_parent = aq_parent(aq_inner(obj))
>>> also_containment_parent = obj.aq_inner.aq_parent
>>> containment_parent is also_containment_parent
TrueThere are two base classes that signal an object's participation in acquisition. Most content objects inherit Acquisition.Implicit, which means that __getattr__() is implemented in such a way that if an attribute is not found on the object, the parents in the (outermost) acquisition chain will be queried. This is the type of acquisition we saw in Chapter 4. Bear in mind that this applies both to containment, and to attributes defined in code. Using implicit acquisition (e.g. during URL traversal), an object contained in a parent folder may be returned if it matches the name of therequested attribute.
If you need to temporarily turn off implicit acquisition, you can use the aq_explicit attribute, which is provided by Acquisition.Implict. We use the aq_base function to discard the acquisition wrapper and get the raw object when testing object reference equivalence. Without aq_base, we would be comparing the object references of the acquisition wrappers themselves, not the objects they wrap.
>>> from Acquisition import aq_base
>>> favorites = getattr(self.portal, 'favorites')
>>> acquiring = getattr(self.folder, 'favorites', None)
>>> aq_base(acquiring) is aq_base(favorites)
True
>>> non_acquiring = getattr(self.folder.aq_explicit, 'favorites',
None)
>>> non_acquiring is None
TrueThe other type of acquisition is explicit acquisition, provided by the Acquisition.Explicit base class. Here, attributes are not acquired from parents in the chain implicitly as they are with Acquisition.Implicit. Explicit acquisition still maintains an acquisition chain, which is crucial for Zope 2 security and containment.Methods such as absolute_url() and getPhysicalPath(), which rely on acquisition to construct a path, continue to work.
An object's acquisition chain is constructed dynamically, during traversal or inspection from code. If an attribute on an acquisition-aware object returns another acquisition-aware object, then the former object is the immediate acquisition chain parent of the returned object. The acquisition chain can be inspected using the aq_ chain function:
>>> from Acquisition import aq_chain, aq_inner
>>> aq_chain(self.portal.favorites)
[<ATDocument at /plone/favorites >, <PloneSite at /plone>,
<Application at >, <ZPublisher.BaseRequest.RequestContainer object at
...>]
>>> aq_chain(self.folder.favorites)
[<ATDocument at /plone/favorites used for /plone/Members/test_user_
1_>, <ATFolder at /plone/Members/test_user_1_>, <ATBTreeFolder at /
plone/Members>, <PloneSite at /plone>, <Application at >, <ZPublisher.
BaseRequest.RequestContainer object at ...>]Notice how the acquisition chain for favorites is different depending on whether it was acquired from self.portal (which also happens to be its containment parent) or self.folder (via implicit acquisition). We can explicitly request the innermost chain, which is normally the containment chain: >>> aq_chain(aq_inner(self.folder.favorites))
[<ATDocument at /plone/favorites>, <PloneSite at /plone>, <Application
at >, <ZPublisher.BaseRequest.RequestContainer object at ...>]Also, notice how the final parent in the acquisition chain that results from URL traversal is a special request container object. This is why you will sometimes see this:request = context.REQUESTHere, the REQUEST variable is implicitly acquired from the context.The example above shows a nice and tidy acquisition chain, based on the containment hierarchy. Now consider another common piece of code:
from Products.Five.browser import BrowserView
class MyView(BrowserView):
def __init__(self, context, request):
self.context = context
self.request = request
def use_context(self):
context = self.context
# Use 'context' or 'self.context' for something
...We will learn about Zope 3 style browser views later in this chapter, but the important points here are that context is a content object, part of the containment hierarchy, and Products.Five.browser.BrowserView inherits from Acquisition.Explicit. Therefore, when we access self.context, it will have two acquisition chains: the innermost chain, which is the containment chain, and the outermost chain that contains the MyView object.If the use_context() method uses the context variable in a way that relies on containment hierarchy acquisition, for example because it conducts an explicit security check or because it needs to acquire a skin layer template or script, it may encounter spurious AttributeErrors. Therefore, you will often see code like this:
def use_context(self):
context = aq_inner(self.context)
# Now use 'context' in place of 'self.context'
...Sometimes you need to control the acquisition chain explicitly. This generally occurs when you are trying to make an object appear as if it came from somewhere other than where it was originally stored, either as part of a security scheme, or to fool implicit acquisition. To do so, you can use the __of__() method, which is part of any acquisition-aware object.In the following example, we instantiate a content object in memory, without connecting it to the containment hierarchy, and then wrap it in the acquisition context of the portal:
>>> from Products.ATContentTypes.content.document import ATDocument
>>> temp_document = ATDocument('temp_document')
>>> aq_chain(temp_document)
[<ATDocument at temp_document>]
>>> aq_chain(temp_document.__of__(self.portal))
[<ATDocument at /plone/temp_document>, <PloneSite at /plone>,
<Application at >, <ZPublisher.BaseRequest.RequestContainer object at
...>]
[size=5]Path Traversal[/size]In page templates or actions, you will sometimes see expressions such as context/guitars/fender. This is using traversal relative to a particular object, employing a similar heuristic to the one described for URL traversal. In fact, path expressions in page templates can also traverse to elements inside dictionaries and lists. This does not apply to the restrictedTraverse()/unrestrictedTraverse() methods.
Acquisition is taken into account here too.
Path traversal can be invoked from Python code relative to a content object, using this syntax:
>>> self.setRoles(('Manager',))
>>> _ = self.portal.invokeFactory('Folder', 'guitars')
>>> _ = self.portal.guitars.invokeFactory('Document', 'fender')
>>> self.portal.unrestrictedTraverse('guitars/fender')
<ATDocument at /plone/guitars/fender>
>>> fender_path = '/'.join(self.portal.guitars.fender.
getPhysicalPath())
>>> fender_path
'/plone/guitars/fender'
>>> self.portal.unrestrictedTraverse(fender_path)
<ATDocument at /plone/guitars/fender>
There is also a restrictedTraverse(), method, which has the same semantics, but performs security checks for each traversal step, in the same way as skin layer page templates and scripts are subject to untrusted code security checks as explained in Chapter 6.