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.

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.predicates.implements selector. You should instead use cubicweb.predicates.is_instance when you want to select on an entity type, or cubicweb.predicates.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.predicates 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.predicates 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.CWUserRDFAdapter(_cw, **kwargs)[source]#
triples()[source]#

return sequence of 3-tuple of rdflib identifiers

class cubicweb.entities.adapters.EntityRDFAdapter(_cw, **kwargs)[source]#

EntityRDFAdapter is to be specialized for each entity that wants to be converted to RDF using the mechanism from cubicweb.rdf

triples()[source]#

return sequence of 3-tuple of rdflib identifiers

uri#

<wrapped by the cachedproperty decorator>

class cubicweb.entities.adapters.IDownloadableAdapter(_cw, **kwargs)[source]#

interface for downloadable entities

download_content_type()[source]#

return MIME type (unicode) of the downloadable content

download_data()[source]#

return actual data (bytes) of the downloadable content

download_encoding()[source]#

return encoding (unicode) of the downloadable content

download_file_name()[source]#

return file name (unicode) of the downloadable content

download_url(**kwargs)[source]#

return a URL to download entity’s content

It should be a unicode object containing url-encoded ASCII.

class cubicweb.entities.adapters.IDublinCoreAdapter(_cw, **kwargs)[source]#
authors()[source]#

Return a suitable description for the author(s) of the entity

creator()[source]#

Return a suitable description for the creator of the entity

date(date_format=None)[source]#

Return latest modification date of entity

description(format='text/plain')[source]#

Return a suitable description for entity

language()[source]#

Return language used by this entity (translated)

long_title()[source]#

Return a more detailled title for entity

title()[source]#

Return a suitable unicode title for entity

type(form='')[source]#

Return the display name for the type of entity (translated)

class cubicweb.entities.adapters.IEmailableAdapter(_cw, **kwargs)[source]#
allowed_massmail_keys()[source]#

returns a set of allowed email substitution keys

The default is to return the entity’s attribute list but you might override this method to allow extra keys. For instance, a Person class might want to return a companyname key.

as_email_context()[source]#

returns the dictionary as used by the sendmail controller to build email bodies.

NOTE: the dictionary keys should match the list returned by the allowed_massmail_keys method.

class cubicweb.entities.adapters.IFTIndexableAdapter(_cw, **kwargs)[source]#

standard adapter to handle fulltext indexing

fti_containers(_done=None)[source]#

return the list of entities to index when handling self.entity

The actual list of entities depends on fulltext_container usage in the datamodel definition

get_words()[source]#

used by the full text indexer to get words to index

this method should only be used on the repository side since it depends on the logilab.database package

Return type

list

Returns

the list of indexable word of this entity

fti_containers(_done=None)[source]#

return the list of entities to index when handling self.entity

The actual list of entities depends on fulltext_container usage in the datamodel definition

get_words()[source]#

used by the full text indexer to get words to index

this method should only be used on the repository side since it depends on the logilab.database package

Return type

list

Returns

the list of indexable word of this entity

class cubicweb.entities.adapters.INotifiableAdapter(_cw, **kwargs)[source]#
notification_references(view)[source]#

used to control References field of email send on notification for this entity. view is the notification view.

Should return a list of eids which can be used to generate message identifiers of previously sent email(s)

class cubicweb.entities.adapters.ISerializableAdapter(_cw, **kwargs)[source]#

Adapter to serialize an entity to a bare python structure that may be directly serialized to e.g. JSON.

class cubicweb.entities.adapters.ITreeAdapter(_cw, **kwargs)[source]#

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:

children(entities=True, sametype=False)[source]#

Return children entities.

According to the entities parameter, return entity objects or the equivalent result set.

children_rql()[source]#

Returns RQL to get the children of the entity.

different_type_children(entities=True)[source]#

Return children entities of different type as this entity.

According to the entities parameter, return entity objects or the equivalent result set.

is_leaf()[source]#

Returns True if the entity does not have any children.

is_root()[source]#

Returns true if the entity is root of the tree (e.g. has no parent).

iterchildren(_done=None)[source]#

Return an iterator over the item’s children.

iterparents(strict=True)[source]#

Return an iterator on the parents of the entity.

parent()[source]#

Returns the parent entity if any, else None (e.g. if we are on the root).

path(**kwargs)#

Returns the list of eids from the root object to this object.

prefixiter(_done=None)[source]#

Return an iterator over the item’s descendants in a prefixed order.

root()[source]#

Return the root entity of the tree.

same_type_children(entities=True)[source]#

Return children entities of the same type as this entity.

According to the entities parameter, return entity objects or the equivalent result set.

class cubicweb.entities.adapters.IUserFriendlyCheckConstraint(*args, **kwargs)[source]#
class cubicweb.entities.adapters.IUserFriendlyError(*args, **kwargs)[source]#
class cubicweb.entities.adapters.IUserFriendlyUniqueTogether(*args, **kwargs)[source]#

More are defined in web/views.