Adapters make it possible to extend the behavior of a class without modifying the class itself. This allows more modular, nice looking code in complex systems where there might be hundreds of methods per class. Some more advantages of this concept are:
The downside is that adapters cannot be found by "exploring" classes or source code. They must be well documented in order to be discoverable.
Read more about adapters in the zope.component README.
Adapters are matched by:
There are two kinds of adapters:
An adapter provides functionality to a class. This functionality becomes available when the interface is queried from the instance of class.
Below is an example how to make a custom "image provider". The image provider provides a list of images for arbitary content.
This is the image provider interface:
from zope.interface import Interface
class IProductImageProvider(Interface):
def getImages(self):
""" Get Images associated with the product.
@return: iterable of Image objects
"""
This is our content class:
class MyShoppableItemType(folder.ATFolder):
""" Buyable physical good with variants of title and price and multiple images
"""
implements(IVariantProduct)
meta_type = "VariantProduct"
schema = VariantProductSchema
This is the adapter for the content class:
import zope.interface
from getpaid.variantsproduct.interfaces.multiimageproduct import IProductImageProvider
class FolderishProductImageProvider(object):
""" Mix-in class which provide product image management functions.
Assume the content itself is a folderish archetype content type and
all contained image objects are product images.
"""
zope.interface.implements(IProductImageProvider)
def __init__(self, context):
# Each adapter takes the object itself as the contruction
# parameter and possibly provides other parameters for the
# interface adaption
self.context = context
def getImages(self):
""" Return a sequence of images.
Perform folder listing and filter image content from it.
"""
images = self.context.listFolderContents(
contentFilter={"portal_type" : "Image"})
return images
Registers the adapter for you custom content type MyShoppableItemType in the configure.zcml file of your product:
<adapter for=".shop.MyShoppableItemType"
provides=".interfaces.IProductImageProvider"
factory=".images.FolderishProductImageProvider" />
Then we can query the adapter and use it. Unit testing example:
def test_get_images(self):
self.loginAsPortalOwner()
self.portal.invokeFactory("MyShoppableItemType", "product")
product = self.portal.product
image_provider = IProductImageProvider(product)
images = image_provider.getImages()
# Not yet any uploaded images
self.assertEqual(len(images), 0)
The following interfaces are useful when registering adapters:
You can specify any number of interface in the <adapter for="" /> attribute. Separate them with spaces or newlines.
Below is a view-like example which registers against:
Emulate view registration (context, request):
<adapter for="zope.interface.Interface zope.publisher.interfaces.browser.IBrowserRequest"
provides="gomobile.mobile.interfaces.IMobileTracker"
factory=".bango.BangoTracker" />
There are two functions that may be used to get an adapter:
getAdapter/queryAdapter arguments:
# Adapter marker interface.
Example registration:
<!-- Register header animation picking logic - override this for your custom logic -->
<adapter
provides="plone.app.headeranimation.interfaces.IHeaderAnimationPicker"
for="plone.app.headeranimation.behaviors.IHeaderBehavior
Products.CMFCore.interfaces.IContentish
zope.publisher.interfaces.browser.IBrowserRequest
"
factory=".picker.RandomHeaderAnimationPicker" />
Corresponding query code, to look up an adapter implementing the interfaces:
from zope.component import getUtility, getAdapter, getMultiAdapter
# header implements IHeaderBehavior
# doc implements Products.CMFCore.interfaces.IContentish
# request implements zope.publisher.interfaces.browser.IBrowserRequest
from Products.CMFCore.interfaces import IContentish
from zope.publisher.interfaces.browser import IBrowserRequest
self.assertTrue(IHeaderBehavior.providedBy(header))
self.assertTrue(IContentish.providedBy(doc))
self.assertTrue(IBrowserRequest.providedBy(self.portal.REQUEST))
# Throws exception if not found
picker = getMultiAdapter((header, doc, self.portal.REQUEST), IHeaderAnimationPicker)
Note
You cannot get adapters on module-level code during import, as the Zope Component Architecture is not yet initialized.
The following code checks whether the IHeaderBehavior adapter is registered correctly:
from zope.component import getGlobalSiteManager
sm = getGlobalSiteManager()
registrations = [a for a in sm.registeredAdapters() if a.provided == IHeaderBehavior ]
self.assertEqual(len(registrations), 1)
Getting all multi-adapters (context, request):
from zope.component import getAdapters
adapters = getAdapters((context, request), provided=Interface)
Warning
This does not list locally-registered adapters such as Zope views.
Local adapters are effective only inside a certain container, such as a folder. They use five.localsitemanager to register themselves.
The source code of this file is hosted on GitHub. Everyone can update and fix errors in this document with few clicks - no downloads needed.
For basic information about updating this manual and Sphinx format please see Writing and updating the manual guide.