doc/book/en/B1020-define-views.en.txt
changeset 1808 aa09e20dd8c0
parent 1693 49075f57cf2c
parent 1807 6d541c610165
child 1810 e95e876be17c
equal deleted inserted replaced
1693:49075f57cf2c 1808:aa09e20dd8c0
     1 .. -*- coding: utf-8 -*-
       
     2 
       
     3 .. _ViewDefinition:
       
     4 
       
     5 Views definition
       
     6 ================
       
     7 
       
     8 This chapter aims to describe the concept of a `view` used all along
       
     9 the development of a web application and how it has been implemented
       
    10 in `CubicWeb`.
       
    11 
       
    12 We'll start with a description of the interface providing you with a basic
       
    13 understanding of the classes and methods available, then detail the view
       
    14 selection principle which makes `CubicWeb` web interface very flexible.
       
    15 
       
    16 A `View` is an object applied to another object such as an entity.
       
    17 
       
    18 Basic class for views
       
    19 ---------------------
       
    20 
       
    21 Class `View` (`cubicweb.common.view`)
       
    22 `````````````````````````````````````
       
    23 
       
    24 This class is an abstraction of a view class, used as a base class for every
       
    25 renderable object such as views, templates, graphic components, etc.
       
    26 
       
    27 A `View` is instantiated to render a result set or part of a result set. `View`
       
    28 subclasses may be parametrized using the following class attributes:
       
    29 
       
    30     * `templatable` indicates if the view may be embeded in a main
       
    31       template or if it has to be rendered standalone (i.e. XML views
       
    32       must not be embeded in the main template for HTML pages)
       
    33     * if the view is not templatable, it should set the `content_type` class
       
    34       attribute to the correct MIME type (text/xhtml by default)
       
    35     * the `category` attribute may be used in the interface to regroup related
       
    36       objects together
       
    37 
       
    38 At instantiation time, the standard `req`, `rset`, and `cursor`
       
    39 attributes are added and the `w` attribute will be set at rendering
       
    40 time.
       
    41 
       
    42 A view writes to its output stream thanks to its attribute `w` (`UStreamIO`).
       
    43 
       
    44 The basic interface for views is as follows (remember that the result set has a
       
    45 tabular structure with rows and columns, hence cells):
       
    46 
       
    47 * `dispatch(**context)`, render the view by calling `call` or
       
    48   `cell_call` depending on the given parameters
       
    49 * `call(**kwargs)`, call the view for a complete result set or null (default 
       
    50   implementation calls `cell_call()` on each cell of the result set)
       
    51 * `cell_call(row, col, **kwargs)`, call the view for a given cell of a result set
       
    52 * `url()`, returns the URL enabling us to get the view with the current
       
    53   result set 
       
    54 * `view(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of identifier 
       
    55   `__vid` on the given result set. It is possible to give a view identifier
       
    56   of fallback that will be used if the view requested is not applicable to the
       
    57   result set
       
    58   
       
    59 * `wview(__vid, rset, __fallback_vid=None, **kwargs)`, similar to `view` except
       
    60   the flow is automatically passed in the parameters
       
    61   
       
    62 * `html_headers()`, returns a list of HTML headers to set by the main template
       
    63 
       
    64 * `page_title()`, returns the title to use in the HTML header `title`
       
    65 
       
    66 
       
    67 Other basic view classes
       
    68 ````````````````````````
       
    69 Here are some of the subclasses of `View` defined in `cubicweb.common.view`
       
    70 that are more concrete as they relate to data rendering within the application:
       
    71 
       
    72 * `EntityView`, view applying to lines or cell containing an entity (e.g. an eid)
       
    73 * `StartupView`, start view that does not require a result set to apply to
       
    74 * `AnyRsetView`, view applied to any result set 
       
    75 * `EmptyRsetView`, view applied to an empty result set
       
    76 
       
    77 
       
    78 The selection view principle
       
    79 ----------------------------
       
    80 
       
    81 A view is essentially defined by:
       
    82 
       
    83 - an identifier (all objects in `CubicWeb` are entered in a registry
       
    84   and this identifier will be used as a key). This is defined in the class
       
    85   attribute ``id``.
       
    86   
       
    87 - a filter to select the result sets it can be applied to. This is defined in
       
    88   the class attribute ``__selectors__``, which expects a tuple of selectors
       
    89   as its value.
       
    90 
       
    91 
       
    92 For a given identifier, multiple views can be defined. `CubicWeb` uses
       
    93 a selector which computes scores to identify and select the
       
    94 best view to apply in the given context. The selectors library is in 
       
    95 ``cubicweb.common.selector`` and a library of the methods used to
       
    96 compute scores is in ``cubicweb.vregistry.vreq``.
       
    97 
       
    98 .. include:: B1021-views-selectors.en.txt
       
    99 
       
   100 Registerer
       
   101 ``````````
       
   102 [Registerers are deprecated: they will soon disappear for explicite 
       
   103 registration...] 
       
   104 
       
   105 A view is also customizable through its attribute ``__registerer__``.
       
   106 This is used at the time the application is launched to manage how
       
   107 objects (views, graphic components, actions, etc.) 
       
   108 are registered in the `cubicWeb` registry.
       
   109 
       
   110 A `registerer` can, for example, identify when we register an 
       
   111 object that is equivalent to an already registered object, which
       
   112 could happen when we define two `primary` views for an entity type.
       
   113 
       
   114 The purpose of a `registerer` is to control object registry
       
   115 at the application startup whereas `selectors` control objects
       
   116 when they are selected for display.
       
   117 
       
   118 
       
   119 .. include:: B1022-views-stdlib.en.txt
       
   120 
       
   121 
       
   122 Examples of views class 
       
   123 -----------------------
       
   124 
       
   125 - Using the attribute `templatable`
       
   126 
       
   127   ::
       
   128     
       
   129 
       
   130     class RssView(XmlView):
       
   131         id = 'rss'
       
   132         title = _('rss')
       
   133         templatable = False
       
   134         content_type = 'text/xml'
       
   135         http_cache_manager = MaxAgeHTTPCacheManager
       
   136         cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
       
   137     
       
   138 
       
   139 
       
   140 - Using the attribute `__selectors__`
       
   141 
       
   142   ::
       
   143     
       
   144 
       
   145     class SearchForAssociationView(EntityView):
       
   146         """view called by the edition view when the user asks
       
   147         to search for something to link to the edited eid
       
   148         """
       
   149         id = 'search-associate'
       
   150         title = _('search for association')
       
   151         __selectors__ = (one_line_rset, match_search_state, accept_selector)
       
   152         accepts = ('Any',)
       
   153         search_states = ('linksearch',)
       
   154 
       
   155     
       
   156 Rendering methods and attributes for ``PrimaryView``
       
   157 ----------------------------------------------------
       
   158 
       
   159 By default, `CubicWeb` provides a primary view for each new entity type
       
   160 you create. The first view you might be interested in modifying.
       
   161 
       
   162 Let's have a quick look at the EntityView ``PrimaryView`` as well as
       
   163 its rendering method::
       
   164     
       
   165     class PrimaryView(EntityView):
       
   166     """the full view of an non final entity"""
       
   167         id = 'primary'
       
   168         title = _('primary')
       
   169         show_attr_label = True
       
   170         show_rel_label = True
       
   171         skip_none = True
       
   172         skip_attrs = ('eid', 'creation_date', 'modification_date')
       
   173         skip_rels = ()
       
   174         main_related_section = True
       
   175 
       
   176         ...
       
   177 
       
   178     def cell_call(self, row, col):
       
   179         self.row = row
       
   180         self.render_entity(self.complete_entity(row, col))
       
   181 
       
   182     def render_entity(self, entity):
       
   183         """return html to display the given entity"""
       
   184         siderelations = []
       
   185         self.render_entity_title(entity)
       
   186         self.render_entity_metadata(entity)
       
   187         # entity's attributes and relations, excluding meta data
       
   188         # if the entity isn't meta itself
       
   189         self.w(u'<div>')
       
   190         self.w(u'<div class="mainInfo">')
       
   191         self.render_entity_attributes(entity, siderelations)
       
   192         self.w(u'</div>')
       
   193         self.content_navigation_components('navcontenttop')
       
   194         if self.main_related_section:
       
   195             self.render_entity_relations(entity, siderelations)
       
   196         self.w(u'</div>')
       
   197         # side boxes
       
   198         self.w(u'<div class="primaryRight">')
       
   199         self.render_side_related(entity, siderelations)
       
   200         self.w(u'</div>')
       
   201         self.w(u'<div class="clear"></div>')
       
   202         self.content_navigation_components('navcontentbottom')
       
   203 
       
   204     ...
       
   205 
       
   206 ``cell_call`` is executed for each entity of a result set and apply ``render_entity``.
       
   207 
       
   208 The methods you want to modify while customizing a ``PrimaryView`` are:
       
   209 
       
   210 *render_entity_title(self, entity)* 
       
   211     Renders the entity title based on the assumption that the method 
       
   212     ``def content_title(self)`` is implemented for the given entity type.
       
   213 
       
   214 *render_entity_metadata(self, entity)*
       
   215     Renders the entity metadata based on the assumption that the method
       
   216     ``def summary(self)`` is implemented for the given entity type.
       
   217 
       
   218 *render_entity_attributes(self, entity, siderelations)*
       
   219     Renders all the attribute of an entity with the exception of attribute
       
   220     of type `Password` and `Bytes`.
       
   221 
       
   222 *content_navigation_components(self, context)*
       
   223     This method is applicable only for entity type implementing the interface 
       
   224     `IPrevNext`. This interface is for entities which can be linked to a previous
       
   225     and/or next entity. This methods will render the navigation links between
       
   226     entities of this type, either at the top or at the bottom of the page
       
   227     given the context (navcontent{top|bottom}).
       
   228 
       
   229 *render_entity_relations(self, entity, siderelations)*
       
   230     Renders all the relations of the entity in the main section of the page.
       
   231         
       
   232 *render_side_related(self, entity, siderelations)*
       
   233     Renders all the relations of the entity in a side box. This is equivalent
       
   234     to *render_entity_relations* in addition to render the relations
       
   235     in a box.
       
   236 
       
   237 Also, please note that by setting the following attributes in you class,
       
   238 you can already customize some of the rendering:
       
   239 
       
   240 *show_attr_label*
       
   241     Renders the attribute label next to the attribute value if set to True.
       
   242     Otherwise, does only display the attribute value.
       
   243 
       
   244 *show_rel_label* 
       
   245     Renders the relation label next to the relation value if set to True.
       
   246     Otherwise, does only display the relation value.
       
   247 
       
   248 *skip_none*
       
   249     Does not render an attribute value that is None if set to True.
       
   250 
       
   251 *skip_attrs*
       
   252     Given a list of attributes name, does not render the value of the attributes listed.
       
   253 
       
   254 *skip_rels*
       
   255     Given a list of relations name, does not render the relations listed.
       
   256 
       
   257 *main_related_section*
       
   258     Renders the relations of the entity if set to True.
       
   259 
       
   260 A good practice is for you to identify the content of your entity type for which
       
   261 the default rendering does not answer your need so that you can focus on the specific
       
   262 method (from the list above) that needs to be modified. We do not recommand you to
       
   263 overwrite ``render_entity`` as you might potentially loose the benefits of the side
       
   264 boxes handling.
       
   265 
       
   266 Example of a view customization
       
   267 -------------------------------
       
   268 
       
   269 [FIXME] XXX Example needs to be rewritten as it shows how to modify cell_call which
       
   270 contredicts our advise of not modifying it.
       
   271 
       
   272 We'll show you now an example of a ``primary`` view and how to customize it.
       
   273 
       
   274 If you want to change the way a ``BlogEntry`` is displayed, just override 
       
   275 the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py`` ::
       
   276 
       
   277   01. from cubicweb.web.views import baseviews
       
   278   02.
       
   279   03. class BlogEntryPrimaryView(baseviews.PrimaryView):
       
   280   04.
       
   281   05.     accepts = ('BlogEntry',)
       
   282   06.
       
   283   07.     def cell_call(self, row, col):
       
   284   08.         entity = self.entity(row, col)
       
   285   09.         self.w(u'<h1>%s</h1>' % entity.title)
       
   286   10.         self.w(u'<p>published on %s in category %s</p>' % \
       
   287   11.                (entity.publish_date.strftime('%Y-%m-%d'), entity.category))
       
   288   12.         self.w(u'<p>%s</p>' % entity.text)
       
   289 
       
   290 The above source code defines a new primary view (`line 03`) for
       
   291 ``BlogEntry`` (`line 05`). 
       
   292 
       
   293 Since views are applied to result sets which can be tables of
       
   294 data, we have to recover the entity from its (row,col)-coordinates (`line 08`).
       
   295 We will get to this in more detail later.
       
   296 
       
   297 The view method ``self.w()`` is used to output data. Here `lines
       
   298 09-12` output HTML tags and values of the entity's attributes.
       
   299 
       
   300 When displaying the same blog entry as before, you will notice that the
       
   301 page is now looking much nicer. [FIXME: it is not clear to what this refers.]
       
   302 
       
   303 .. image:: images/lax-book.09-new-view-blogentry.en.png
       
   304    :alt: blog entries now look much nicer
       
   305 
       
   306 Let us now improve the primary view of a blog ::
       
   307 
       
   308   01. class BlogPrimaryView(baseviews.PrimaryView):
       
   309   02. 
       
   310   03.     accepts = ('Blog',)
       
   311   04.
       
   312   05.     def cell_call(self, row, col):
       
   313   06.         entity = self.entity(row, col)
       
   314   07.         self.w(u'<h1>%s</h1>' % entity.title)
       
   315   08.         self.w(u'<p>%s</p>' % entity.description)
       
   316   09.         rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid)
       
   317   10.         self.wview('primary', rset)
       
   318 
       
   319 In the above source code, `lines 01-08` are similar to the previous
       
   320 view we defined. [FIXME: defined where ?]
       
   321 
       
   322 At `line 09`, a simple request is made to build a result set with all
       
   323 the entities linked to the current ``Blog`` entity by the relationship
       
   324 ``entry_of``. The part of the framework handling the request knows
       
   325 about the schema and infer that such entities have to be of the
       
   326 ``BlogEntry`` kind and retrieves them.
       
   327 
       
   328 The request returns a selection of data called a result set. At 
       
   329 `line 10` the view 'primary' is applied to this result set to output
       
   330 HTML. 
       
   331 
       
   332 **This is to be compared to interfaces and protocols in object-oriented
       
   333 languages. Applying a given view called 'a_view' to all the entities
       
   334 of a result set only requires to have for each entity of this result set,
       
   335 an available view called 'a_view' which accepts the entity.**
       
   336 
       
   337 Assuming we added entries to the blog titled `MyLife`, displaying it
       
   338 now allows to read its description and all its entries.
       
   339 
       
   340 .. image:: images/lax-book.10-blog-with-two-entries.en.png
       
   341    :alt: a blog and all its entries
       
   342 
       
   343 **Before we move forward, remember that the selection/view principle is
       
   344 at the core of `CubicWeb`. Everywhere in the engine, data is requested
       
   345 using the RQL language, then HTML/XML/text/PNG is output by applying a
       
   346 view to the result set returned by the query. That is where most of the
       
   347 flexibility comes from.**
       
   348 
       
   349 [WRITE ME]
       
   350 
       
   351 * implementing interfaces, calendar for blog entries
       
   352 * show that a calendar view can export data to ical
       
   353 
       
   354 We will implement the `cubicweb.interfaces.ICalendarable` interfaces on
       
   355 entities.BlogEntry and apply the OneMonthCalendar and iCalendar views
       
   356 to result sets like "Any E WHERE E is BlogEntry"
       
   357 
       
   358 * create view "blogentry table" with title, publish_date, category
       
   359 
       
   360 We will show that by default the view that displays 
       
   361 "Any E,D,C WHERE E publish_date D, E category C" is the table view.
       
   362 Of course, the same can be obtained by calling
       
   363 self.wview('table',rset)
       
   364 
       
   365 * in view blog, select blogentries and apply view "blogentry table"
       
   366 * demo ajax by filtering blogentry table on category
       
   367 
       
   368 we did the same with 'primary', but with tables we can turn on filters
       
   369 and show that ajax comes for free.
       
   370 [FILLME]
       
   371 
       
   372 
       
   373 Templates
       
   374 ---------
       
   375 
       
   376 *Templates* are specific views that do not depend on a result set. The basic
       
   377 class `Template` (`cubicweb.common.view`) is derived from the class `View`.
       
   378 
       
   379 To build a HTML page, a *main template* is used. In general, the template of
       
   380 identifier `main` is the one to use (it is not used in case an error is raised or for
       
   381 the login form for example). This template uses other templates in addition
       
   382 to the views which depends on the content to generate the HTML page to return.
       
   383 
       
   384 A *template* is responsible for:
       
   385 
       
   386 1. executing RQL query of data to render if necessary
       
   387 2. identifying the view to use to render data if it is not specified
       
   388 3. composing the HTML page to return
       
   389 
       
   390 You will find out more about templates in :ref:`templates`. 
       
   391 
       
   392 XML views, binaries...
       
   393 ----------------------
       
   394 For views generating other formats than HTML (an image generated dynamically
       
   395 for example), and which can not simply be included in the HTML page generated
       
   396 by the main template (see above), you have to:
       
   397 
       
   398 * set the attribute `templatable` of the class to `False`
       
   399 * set, through the attribute `content_type` of the class, the MIME type generated
       
   400   by the view to `application/octet-stream`
       
   401 
       
   402 For views dedicated to binary content creation (like dynamically generated 
       
   403 images), we have to set the attribute `binary` of the class to `True` (which
       
   404 implies that `templatable == False`, so that the attribute `w` of the view could be
       
   405 replaced by a binary flow instead of unicode).
       
   406 
       
   407 (X)HTML tricks to apply
       
   408 -----------------------
       
   409 
       
   410 Some web browser (Firefox for example) are not happy with empty `<div>`
       
   411 (by empty we mean that there is no content in the tag, but there
       
   412 could be attributes), so we should always use `<div></div>` even if
       
   413 it is empty and not use `<div/>`.
       
   414