src/source/dev_guide/howto-adapter.rst
branchdoc-dc
changeset 142 5a82a9a2ea46
parent 141 9ab9f762abed
child 143 1d052540f8cd
--- a/src/source/dev_guide/howto-adapter.rst	Fri Dec 21 15:52:29 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-.. _adapterhowto:
-
-
-How to define an annotations adapter?
-=====================================
-
-Adapters are important concepts of ZCA and PyAMS framework. If you don't know what are adapters, see :ref:`zca`.
-
-
-What are annotations?
-+++++++++++++++++++++
-
-When an adapter have to add persistent attributes to a persistent class, it can add these attributes directly into
-persistent instances. But this way can lead to conflicts when several adapters want to use the same attribute name for
-different kinds of information.
-
-Annotations are an elegant way to handle this use case: they are based on a BTree which is stored into a
-specific instance attribute (*__annotations__*). Any adapter can then use this dictionary to store it's own
-informations, using it's own namespace as dictionary key.
-
-ZODB browser allows you to display existing annotations:
-
-.. image:: ../_static/annotations-1.png
-
-This example displays several annotations, each using it's own namespace:
-
-.. image:: ../_static/annotations-2.png
-
-
-Designing interfaces
-++++++++++++++++++++
-
-The first step with ZCA is to design your interfaces.
-
-The are going to base our example on PyAMS_content 'paragraphs' component: a content class is marked as a
-*paragraphs container target*, a class that can store paragraphs. But the real storage of paragraphs is done by
-another *container* class:
-
-.. code-block:: python
-    :linenos:
-
-    from zope.annotation.interfaces import IAttributeAnnotatable
-    from zope.containers.constraints import containers, contains
-
-
-    class IBaseParagraph(Interface):
-        """Base paragraph interface"""
-
-        containers('.IParagraphContainer')
-
-
-    class IParagraphContainer(IOrderedContainer):
-        """Paragraphs container"""
-
-        contains(IBaseParagraph)
-
-
-    class IParagraphContainerTarget(IAttributeAnnotatable):
-        """Paragraphs container marker interface"""
-
-
-    PARAGRAPH_CONTAINER_KEY = 'pyams_content.paragraph'
-
-
-- line 5 to 8: :class:`IBaseParagraph` is the base interface for all paragraphs; constraint implies that paragraphs
-  can only be stored in a container implementing :class:`IParagraphContainer` interface.
-- line 11 to 14: :class:`IParagraphContainer` is the base interface for paragraphs containers; constraint implies that
-  such a container can only contain objects implementing :class:`IBaseParagraph` interface.
-- line 17 to 18: :class:`IParagraphContainerTarget` is only a *marker* interface which doesn't provide any method or
-  attribute; it only inherits from :class:`IAttributeAnnotatable`, which implies that classes implementing this
-  interface allows other classes to add informations as annotations through a dedicated *__annotations__* attribute.
-- line 21: this is the key which will be used to store our annotation.
-
-
-Creating persistent classes
-+++++++++++++++++++++++++++
-
-The first step is to declare that a given content class can store paragraphs:
-
-.. code-block:: python
-    :linenos:
-
-    from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
-    from zope.interface import implementer
-
-    @implementer(IParagraphContainerTarget)
-    class WfNewsEvent(WfSharedContent):
-        """News event class"""
-
-Here we just say "Well, I'm a shared content, and I'm OK to store paragraphs!".
-
-So we can design the paragraphs container class. It's this class which will *really* store the paragraphs:
-
-.. code-block:: python
-    :linenos:
-
-    @implementer(IParagraphContainer)
-    class ParagraphContainer(BTreeOrderedContainer):
-        """Paragraphs container"""
-
-The paragraphs container class inherits from a :class:`BTreeOrderedContainer` and implements
-:class:`IParagraphContainer`.
-
-The last operation is to create the adapter, which is the *glue* between the *target* class and the paragraphs
-container:
-
-.. code-block:: python
-    :linenos:
-
-    from pyams_utils.adapter import adapter_config, get_annotation_adapter
-
-    @adapter_config(context=IParagraphContainerTarget, provides=IParagraphContainer)
-    def paragraph_container_factory(target):
-        """Paragraphs container factory"""
-        return get_annotation_adapter(target,
-                                      PARAGRAPH_CONTAINER_KEY,
-                                      ParagraphContainer,
-                                      name='++paras++')
-
-PyAMS provides a shortcut to create an annotation adapter in :func:`pyams_utils.adapter.get_annotation_adapter`.
-It's mandatory arguments are:
-
-- **context** (line 6): the context to which the adapter is applied
-- **key** (line 7): the string key used to access and store context's annotations
-- **factory** (line 8): if the requested annotation is missing, a new one is created using this factory (which can be a class or
-  a function)
-
-Optional arguments are:
-
-- **markers** (None by default): if set, should be a list of marker interfaces which will be assigned to object
-  created by the factory
-- **notify**: if *True* (default), an :class:`ObjectCreatedEvent` event is notified on object creation
-- **locate**: if *True* (default), context is set as *parent* of created object
-- **parent**: if *locate* is True and if *parent* is set, this is the object to which the new object should be *parented*
-  instead of initial context
-- **name** (None by default): some objects need to be traversed, especially when you have to be able to access them through an URL; this
-  is the name given to created object.
-
-
-Using your adapter
-++++++++++++++++++
-
-Starting from your *content* object, it's then very simple to access to the paragraphs container:
-
-.. code-block:: python
-    :linenos:
-
-    event = WfNewsEvent()
-    paragraphs_container = IParagraphContainer(event, None)
-
-And that's it! From now I can get access to all paragraphs associated with my initial content!!
-
-
-Managing traversal
-++++++++++++++++++
-
-As said before, sometimes you have to be able to *traverse* from an initial content to a given sub-content
-managed by an adapter.
-
-PyAMS defines a custom :class:`pyams_utils.traversing.NamespaceTraverser`: when a request traversing subpath is
-starting with '++' characters, it is looking for a named traverser providing :class:`ITraversable` interface
-to the last traversed object.
-
-.. code-block:: python
-    :linenos:
-
-    @adapter_config(name='paras', context=IParagraphContainerTarget, provides=ITraversable)
-    class ParagraphContainerNamespace(ContextAdapter):
-        """++paras++ namespace adapter"""
-
-        def traverse(self, name, furtherpath=None):
-            return IParagraphContainer(self.context)
-
-- line 1: the adapter is named "paras"; this is matching the *++paras++* name which was given to our annotation adapter
-- line 2: the adapter is just a simple context adapter, so inheriting from :class:`pyams_utils.adapter.ContextAdapter`
-- lines 5 to 6: the *traverse* method is used to access the adapted content; if a name like "++ns++value" is given
-  to an adapted object, the "value" part is given as *name" argument.
-
-From now, as soon as an URL like "/mycontent/++paras++/" will be used, you will get access to the paragraphs container.
-This is a standard BTree container, so will get access to it's sub-objects by key.