diff -r 875bdc0fe8ce -r 105011657405 doc/book/en/devweb/facets.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/facets.rst Fri Apr 23 17:31:46 2010 +0200 @@ -0,0 +1,172 @@ +The facets system +----------------- + +Facets allow to restrict searches according to some criteria. CubicWeb +has a builtin `facet`_ system to define restrictions `filters`_ really +as easily as possible. A few base classes for facets are provided in +``cubicweb.web.facet.py``. All classes inherits from the base class +``AbstractFacet``. + +Here is an overview of the facets rendering pick from the `tracker` cube: + +.. image:: ../images/facet_overview.png + +Facets will appear on each page presenting more than one entity. + + + +VocabularyFacet +~~~~~~~~~~~~~~~~ +The ``VocabularyFacet`` inherits from the ``AbstractFacet``. +A class which inherits from VocabularyFacets must redefine these methods: + +.. automethod:: cubicweb.web.facet.VocabularyFacet.vocabulary +.. automethod:: cubicweb.web.facet.VocabularyFacet.possible_values + +RelationFacet +~~~~~~~~~~~~~~ + +The ``RelationFacet`` inherits from the ``VocabularyFacet``. It allows to filter entities according to certain relation's values. Generally, you just have to define some class attributes like: + +- rtype: the name of the relation +- role: the default value is set to `subject` +- target_attr: needed if it is not the default attribute of the entity + + +To illustrate this facet, let's take for example an *excerpt* of the schema of an office location search application: + +.. sourcecode:: python + + class Office(WorkflowableEntityType): + price = Int(description='euros / m2 / HC / HT') + surface = Int(description='m2') + description = RichString(fulltextindexed=True) + has_address = SubjectRelation('PostalAddress', + cardinality='1?', + composite='subject') + proposed_by = SubjectRelation('Agency') + comments = ObjectRelation('Comment', + cardinality='1*', + composite='object') + screenshots = SubjectRelation(('File', 'Image'), + cardinality='*1', + composite='subject') + + +We define a facet to filter offices according to the attribute +`postalcode` of their associated `PostalAdress`. + +.. sourcecode:: python + + class PostalCodeFacet(RelationFacet): + __regid__ = 'postalcode-facet' # every registered class must have an id + __select__ = implements('Office') # this facet should only be selected when + # visualizing offices + rtype = 'has_address' # this facet is a filter on the entity linked to + # the office thrhough the relation + # has_address + target_attr = 'postalcode' # the filter's key is the attribute "postal_code" + # of the target PostalAddress entity + + +AttributeFacet +~~~~~~~~~~~~~~ + +The ``AttributeFacet`` inherits from the ``RelationFacet``. It allows to filter entities according to certain attribute's values. + +The example below resumes the former schema. We define now a filter based on the `surface` attribute of the +`Office`. + +.. sourcecode:: python + + class SurfaceFacet(AttributeFacet): + __regid__ = 'surface-facet' # every registered class must have an id + __select__ = implements('Office') # this facet should only be selected when + # visualizing offices + rtype = 'surface' # the filter's key is the attribute "surface" + comparator = '>=' # override the default value of operator since + # we want to filter according to a + # minimal + # value, not an exact one + + def rset_vocabulary(self, ___): + """override the default vocabulary method since we want to hard-code + our threshold values. + Not overriding would generate a filter box with all existing surfaces + defined in the database. + """ + return [('> 200', '200'), ('> 250', '250'), + ('> 275', '275'), ('> 300', '300')] + +RangeFacet +~~~~~~~~~~ +The ``RangeFacet`` inherits from the ``AttributeFacet``. It allows to filter entities according to certain attributes of numerical type. + +The ``RangeFacet`` displays a slider using `jquery`_ to choose a lower bound and an upper bound. + +The example below defines a facet to filter a selection of books according to their number of pages. + +.. sourcecode:: python + + class BookPagesFacet(RangeFacet): + __regid__ = 'priority-facet' + __select__ = RangeFacet.__select__ & implements('Book') + rtype = 'pages' + +The image below display the rendering of the ``RangeFacet``: + +.. image:: ../images/facet_range.png + +DateRangeFacet +~~~~~~~~~~~~~~ +The ``DateRangeFacet`` inherits from the ``RangeFacet``. It allows to filter entities according to certain attributes of date type. + +Here is an example of code that defines a facet to filter +musical works according to their composition date: + +.. sourcecode:: python + + class CompositionDateFacet(DateRangeFacet): + # 1. make sure this facet is displayed only on Track selection + __select__ = DateRangeFacet.__select__ & implements('Track') + # 2. give the facet an id required by CubicWeb) + __regid__ = 'compdate-facet' + # 3. specify the attribute name that actually stores the date in the DB + rtype = 'composition_date' + +With this facet, on each page displaying tracks, you'll be able to filter them +according to their composition date with a jquery slider. + +The image below display the rendering of the ``DateRangeFacet``: + +.. image:: ../images/facet_date_range.png + + +HasRelationFacet +~~~~~~~~~~~~~~~~ + +The ``DateRangeFacet`` inherits from the ``AbstractFacet``. It will +display a simple checkbox and lets you refine your selection in order +to get only entities that actually use this relation. + +Here is an example of the rendering of the ``HasRelationFacet`` to +filter entities with image and the corresponding code: + +.. image:: ../images/facet_has_image.png + +.. sourcecode:: python + + class HasImageFacet(HasRelationFacet): + __regid__ = 'hasimage-facet' + __select__ = HasRelationFacet.__select__ & implements('Book') + rtype = 'has_image' + + + +To use ``HasRelationFacet`` on a reverse relation add ``role = 'object'`` in +it's definitions. + +.. _facet: http://en.wikipedia.org/wiki/Faceted_browser +.. _filters: http://www.cubicweb.org/blogentry/154152 +.. _jquery: http://www.jqueryui.com/ +