1 The facets system |
|
2 ----------------- |
|
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 __regid__ = '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 __regid__ = '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 __regid__ = '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. |
|