doc/book/en/development/devweb/facets.rst
branchstable
changeset 3991 a0aa9789c6bc
parent 1714 a721966779be
child 4437 21f2e01fdd6a
equal deleted inserted replaced
3990:14e14fef4460 3991:a0aa9789c6bc
     1 The facets system
     1 The facets system
     2 -----------------
     2 -----------------
     3 XXX feed me
     3 XXX feed me more (below is the extracted of adim blog)
       
     4 
       
     5 
       
     6 Recently, for internal purposes, we've made a little cubicweb application to
       
     7 help us
       
     8 organizing visits to find new office locations. Here's an *excerpt* of the
       
     9 schema:
       
    10 
       
    11 .. sourcecode:: python
       
    12 
       
    13   class Office(WorkflowableEntityType):
       
    14       price = Int(description='euros / m2 / HC / HT')
       
    15       surface = Int(description='m2')
       
    16       description = RichString(fulltextindexed=True)
       
    17       has_address = SubjectRelation('PostalAddress', cardinality='1?', composite='subject')
       
    18       proposed_by = SubjectRelation('Agency')
       
    19       comments = ObjectRelation('Comment', cardinality='1*', composite='object')
       
    20       screenshots = SubjectRelation(('File', 'Image'), cardinality='*1',
       
    21                                     composite='subject')
       
    22 
       
    23 The two other entity types defined in the schema are `Visit` and `Agency` but we
       
    24 can also guess from the above that this application uses the two cubes
       
    25 `comment`_ and
       
    26 `addressbook`_ (remember, cubicweb is only a game where you assemble cubes !). 
       
    27 
       
    28 While we know that just defining the schema in enough to have a full, usable,
       
    29 (testable !) application, we also know that every application needs to be 
       
    30 customized to fulfill the needs it was built for. So in this case, what we
       
    31 needed most was some custom filters that would let us restrict searches
       
    32 according
       
    33 to surfaces, prices or zipcodes. Fortunately for us, Cubicweb provides the
       
    34 **facets** (image_) mechanism and a few base classes that make the task quite
       
    35 easy:
       
    36 
       
    37 .. sourcecode:: python
       
    38 
       
    39   class PostalCodeFacet(RelationFacet): 
       
    40       id = 'postalcode-facet'             # every registered class must have an id
       
    41       __select__ = implements('Office')   # this facet should only be selected when 
       
    42                                           # visualizing offices
       
    43       rtype = 'has_address'               # this facet is a filter on the entity linked to
       
    44                                           # the office thrhough the relation
       
    45                                           # has_address
       
    46       target_attr = 'postalcode'          # the filter's key is the attribute "postal_code"
       
    47                                           # of the target PostalAddress entity
       
    48 
       
    49 This is a typical `RelationFacet`: we want to be able to filter offices
       
    50 according
       
    51 to the attribute `postalcode` of their associated `PostalAdress`. Each line in
       
    52 the class is explained by the comment on its right.
       
    53 
       
    54 Now, here is the code to define a filter based on the `surface` attribute of the
       
    55 `Office`:
       
    56 
       
    57 .. sourcecode:: python
       
    58 
       
    59   class SurfaceFacet(AttributeFacet):
       
    60       id = 'surface-facet'              # every registered class must have an id
       
    61       __select__ = implements('Office') # this facet should only be selected when 
       
    62                                         # visualizing offices
       
    63       rtype = 'surface'                 # the filter's key is the attribute "surface" 
       
    64       comparator = '>='                 # override the default value of operator since 
       
    65                                         # we want to filter according to a
       
    66                                         # minimal 
       
    67                                         # value, not an exact one
       
    68 
       
    69       def rset_vocabulary(self, ___):
       
    70           """override the default vocabulary method since we want to hard-code
       
    71           our threshold values. 
       
    72           Not overriding would generate a filter box with all existing surfaces
       
    73           defined in the database.
       
    74           """
       
    75           return [('> 200', '200'), ('> 250', '250'),
       
    76                   ('> 275', '275'), ('> 300', '300')]
       
    77 
       
    78 
       
    79 And that's it: we have two filter boxes automatically displayed on each page
       
    80 presenting more than one office. The `price` facet is basically the same as the
       
    81 `surface` one but with a different vocabulary and with ``rtype = 'price'``.
       
    82 
       
    83 (The cube also benefits from the builtin google map views defined by
       
    84 cubicweb but that's for another blog).
       
    85 
       
    86 .. _image: http://www.cubicweb.org/image/197646?vid=download
       
    87 .. _comment: http://www.cubicweb.org/project/cubicweb-comment
       
    88 .. _addressbook: http://www.cubicweb.org/project/cubicweb-addressbook
       
    89 
       
    90 CubicWeb has this really nice builtin `facet`_ system to
       
    91 define restrictions `filters`_ really as easily as possible.
       
    92 
       
    93 We've just added two new kind of facets in CubicWeb :
       
    94 
       
    95 - The **RangeFacet** which displays a slider using `jquery`_
       
    96   to choose a lower bound and an upper bound. The **RangeWidget** 
       
    97   works with either numerical values or date values
       
    98 
       
    99 - The **HasRelationFacet** which displays a simple checkbox and
       
   100   lets you refine your selection in order to get only entities
       
   101   that actually use this relation.
       
   102 
       
   103 .. image :: http://www.cubicweb.org/Image/343498?vid=download
       
   104 
       
   105 
       
   106 Here's an example of code that defines a facet to filter 
       
   107 musical works according to their composition date:
       
   108 
       
   109 .. sourcecode:: python
       
   110 
       
   111     class CompositionDateFacet(DateRangeFacet):
       
   112         # 1. make sure this facet is displayed only on Track selection
       
   113         __select__ = DateRangeFacet.__select__ & implements('Track')
       
   114         # 2. give the facet an id (required by CubicWeb)
       
   115         id = 'compdate-facet'
       
   116         # 3. specify the attribute name that actually stores the date in the DB
       
   117         rtype = 'composition_date'
       
   118 
       
   119 And that's it, on each page displaying tracks, you'll be able to filter them
       
   120 according to their composition date with a jquery slider.
       
   121 
       
   122 All this, brought by CubicWeb (in the next 3.3 version)
       
   123 
       
   124 .. _facet: http://en.wikipedia.org/wiki/Faceted_browser
       
   125 .. _filters: http://www.cubicweb.org/blogentry/154152
       
   126 .. _jquery: http://www.jqueryui.com/
       
   127 
       
   128 To use **HasRelationFacet** on a reverse relation add ``role = 'object'`` in
       
   129 it's definitions.