4.6. Interfaces and Adapters — CubicWeb 3.13.2

### Navigation

- index
- modules |
- next |
- previous |
- CubicWeb 3.13.2 »
- Repository development »
- 4. Data as objects »

# 4.6. Interfaces and Adapters

Interfaces are the same thing as object-oriented programming interfaces. Adapter refers to a well-known adapter design pattern that helps separating concerns in object oriented applications.

In CubicWeb adapters provide logical functionalities to entity types. They are introduced in version 3.9. Before that one had to implement Interfaces in entity classes to achieve a similar goal. However, the problem with this approach is that is clutters the entity class’s namespace, exposing name collision risks with schema attributes/relations or even methods names (different interfaces may define the same method with not necessarily the same behaviour expected).

Definition of an adapter is quite trivial. An excerpt from cubicweb itself (found in `cubicweb.entities.adapters`):

    class ITreeAdapter(EntityAdapter):
        """This adapter has to be overriden to be configured using the
        tree_relation, child_role and parent_role class attributes to
        benefit from this default implementation
        """
        __regid__ = 'ITree'

        child_role = 'subject'
        parent_role = 'object'

        def children_rql(self):
            """returns RQL to get children """
            return self.entity.cw_related_rql(self.tree_relation, self.parent_role)

The adapter object has `self.entity` attribute which represents the entity being adapted.

Note

Adapters came with the notion of service identified by the registry identifier of an adapters, hence dropping the need for explicit interface and the `cubicweb.selectors.implements` selector. You should instead use `cubicweb.selectors.is_instance` when you want to select on an entity type, or `cubicweb.selectors.adaptable` when you want to select on a service.

## 4.6.1. Specializing and binding an adapter

    from cubicweb.entities.adapters import ITreeAdapter

    class MyEntityITreeAdapter(ITreeAdapter):
        __select__ = is_instance('MyEntity')
        tree_relation = 'filed_under'

The ITreeAdapter here provides a default implementation. The tree_relation class attribute is actually used by this implementation to help implement correct behaviour.

Here we provide a specific implementation which will be bound for `MyEntity` entity type (the adaptee).

## 4.6.2. Converting code from Interfaces/Mixins to Adapters

Here we go with a small example. Before:

    from cubicweb.selectors import implements
    from cubicweb.interfaces import ITree
    from cubicweb.mixins import ITreeMixIn

    class MyEntity(ITreeMixIn, AnyEntity):
        __implements__ = AnyEntity.__implements__ + (ITree,)

    class ITreeView(EntityView):
        __select__ = implements('ITree')
        def cell_call(self, row, col):
            entity = self.cw_rset.get_entity(row, col)
            children = entity.children()

After:

    from cubicweb.selectors import adaptable, is_instance
    from cubicweb.entities.adapters import ITreeAdapter

    class MyEntityITreeAdapter(ITreeAdapter):
        __select__ = is_instance('MyEntity')

    class ITreeView(EntityView):
        __select__ = adaptable('ITree')
        def cell_call(self, row, col):
            entity = self.cw_rset.get_entity(row, col)
            itree = entity.cw_adapt_to('ITree')
            children = itree.children()

As we can see, the interface/mixin duality disappears and the entity class itself is completely freed from these concerns. When you want to use the ITree interface of an entity, call its cw_adapt_to method to get an adapter for this interface, then access to members of the interface on the adapter

Let’s look at an example where we defined everything ourselves. We start from:

    class IFoo(Interface):
        def bar(self, *args):
            raise NotImplementedError

    class MyEntity(AnyEntity):
        __regid__ = 'MyEntity'
        __implements__ = AnyEntity.__implements__ + (IFoo,)

        def bar(self, *args):
            return sum(captain.age for captain in self.captains)

    class FooView(EntityView):
       __regid__ = 'mycube.fooview'
       __select__ = implements('IFoo')

        def cell_call(self, row, col):
            entity = self.cw_rset.get_entity(row, col)
            self.w('bar: %s' % entity.bar())

Converting to:

    class IFooAdapter(EntityAdapter):
        __regid__ = 'IFoo'
        __select__ = is_instance('MyEntity')

        def bar(self, *args):
            return sum(captain.age for captain in self.entity.captains)

    class FooView(EntityView):
       __regid__ = 'mycube.fooview'
       __select__ = adaptable('IFoo')

         def cell_call(self, row, col):
             entity = self.cw_rset.get_entity(row, col)
             self.w('bar: %s' % entity.cw_adapt_to('IFoo').bar())

Note

When migrating an entity method to an adapter, the code can be moved as is except for the self of the entity class, which in the adapter must become self.entity.

## 4.6.3. Adapters defined in the library

some basic entity adapter implementations, for interfaces used in the framework itself.

class `cubicweb.entities.adapters.` `IDownloadableAdapter`(_cw, **kwargs)
  interface for downloadable entities

class `cubicweb.entities.adapters.` `IProgressAdapter`(_cw, **kwargs)

  something that has a cost, a state and a progression.

  You should at least override progress_info an in_progress methods on concret implementations.

class `cubicweb.entities.adapters.` `ITreeAdapter`(_cw, **kwargs)

  This adapter has to be overriden to be configured using the tree_relation, child_role and parent_role class attributes to benefit from this default implementation.

  This adapter provides a tree interface. It has to be overriden to be configured using the tree_relation, child_role and parent_role class attributes to benefit from this default implementation.

  This class provides the following methods:

More are defined in web/views.

### Navigation

- index
- modules |
- next |
- previous |
- CubicWeb 3.13.2 »
- Repository development »
- 4. Data as objects »