doc/book/en/B1020-define-views.en.txt
author Sandrine Ribeau <sandrine.ribeau@logilab.fr>
Tue, 23 Dec 2008 13:38:02 -0800
changeset 291 87c8d96f6173
parent 288 487bcc269d50
child 292 2d9e83c34b23
permissions -rw-r--r--
[doc] Renaming for advanced thematic.

.. -*- coding: utf-8 -*-

.. _DefinitionVues:

Views definition
================

Basic class for views
---------------------

Class `View` (`cubicweb.common.view`)
`````````````````````````````````````

A view writes in its output exit thanks to its attribute `w` (`UStreamIO`).

The basic interface for views is as follows:

* `dispatch(**context)`, render the view by calling `call` or
  `cell_call` depending on the given parameters
* `call(**kwargs)`, call the view for a complete result set or null
* `cell_call(row, col, **kwargs)`, call the view for a given cell of a result set
* `url()`, returns the URL enabling us to get the view with the current
  result set 
* `view(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of identifier 
  `__vid` on the given result set. It is possible to give a view identifier
  of fallback that will be used if the view requested is not applicable to the
  result set
  
* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, similar to `view` except
  the flow is automatically passed in the parameters
  
* `html_headers()`, returns a list of HTML headers to set by the main template

* `page_title()`, returns the title to use in the HTML header `title`

* `creator(eid)`, returns the eid and the login of the entity creator of the entity
  having the eid given in the parameter 

Other basic classes:

* `EntityView`, view applying to lines or cell containing an entity (e.g. an eid)
* `StartupView`, start view that does not require a result set to apply to
* `AnyRsetView`, view applied to any result set 


The selection view principle
----------------------------

A view includes :

- an identifier (all objects in `LAX` are entered in a registry
  and this identifier will be used as a key)
  
- a filter to select the resulsets it can be applied to


For a given identifier, multiple views can be defined. `CubicWeb` uses
a selector which computes scores so that it can identify and select the
best view to apply in context. The selector library is in 
``cubicweb.common.selector`` and a library of the methods used to
compute scores is in ``cubicweb.vregistry.vreq``.


`CubicWeb` provides a lot of standard views, for a complete list, you
will have to read the code in directory ``cubicweb/web/views/`` (XXX
improve doc).

For example, the view named ``primary`` is the one used to display
a single entity.

If you want to change the way a ``BlogEntry`` is displayed, just
override the view ``primary`` in ``BlogDemo/views.py`` ::

  01. from ginco.web.views import baseviews
  02.
  03. class BlogEntryPrimaryView(baseviews.PrimaryView):
  04.
  05.     accepts = ('BlogEntry',)
  06.
  07.     def cell_call(self, row, col):
  08.         entity = self.entity(row, col)
  09.         self.w(u'<h1>%s</h1>' % entity.title)
  10.         self.w(u'<p>published on %s in category %s</p>' % \
  11.                (entity.publish_date.strftime('%Y-%m-%d'), entity.category))
  12.         self.w(u'<p>%s</p>' % entity.text)

The above source code defines a new primary view (`line 03`) for
``BlogEntry`` (`line 05`). 

Since views are applied to resultsets and resulsets can be tables of
data, it is needed to recover the entity from its (row,col)
coordinates (`line 08`). We will get to this in more detail later.

The view has a ``self.w()`` method that is used to output data. Here `lines
09-12` output HTML tags and values of the entity's attributes.

When displaying same blog entry as before, you will notice that the
page is now looking much nicer.

.. image:: images/lax-book.09-new-view-blogentry.en.png
   :alt: blog entries now look much nicer

Let us now improve the primary view of a blog ::

  01. class BlogPrimaryView(baseviews.PrimaryView):
  02. 
  03.     accepts = ('Blog',)
  04.
  05.     def cell_call(self, row, col):
  06.         entity = self.entity(row, col)
  07.         self.w(u'<h1>%s</h1>' % entity.title)
  08.         self.w(u'<p>%s</p>' % entity.description)
  09.         rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid)
  10.         self.wview('primary', rset)

In the above source code, `lines 01-08` are similar to the previous
view we defined.

At `line 09`, a simple request in made to build a resultset with all
the entities linked to the current ``Blog`` entity by the relationship
``entry_of``. The part of the framework handling the request knows
about the schema and infer that such entities have to be of the
``BlogEntry`` kind and retrieves them.

The request returns a selection of data called a resultset. At 
`line 10` the view 'primary' is applied to this resultset to output
HTML. 

**This is to be compared to interfaces and protocols in object-oriented
languages. Applying a given view to all the entities of a resultset only
requires the availability, for each entity of this resultset, of a
view with that name that can accepts the entity.**

Assuming we added entries to the blog titled `MyLife`, displaying it
now allows to read its description and all its entries.

.. image:: images/lax-book.10-blog-with-two-entries.en.png
   :alt: a blog and all its entries

**Before we move forward, remember that the selection/view principle is
at the core of `CubicWeb`. Everywhere in the engine, data is requested
using the RQL language, then HTML/XML/text/PNG is output by applying a
view to the resultset returned by the query. That is where most of the
flexibility comes from.**

[WRITE ME]

* implementing interfaces, calendar for blog entries
* show that a calendar view can export data to ical

We will implement the cubicwweb.interfaces.ICalendarable interfaces on
entities.BloEntry and apply the OneMonthCalendar and iCalendar views
to resultsets like "Any E WHERE E is BlogEntry"

* create view "blogentry table" with title, publish_date, category

We will show that by default the view that displays 
"Any E,D,C WHERE E publish_date D, E category C" is the table view.
Of course, the same can be obtained by calling
self.wview('table',rset)

* in view blog, select blogentries and apply view "blogentry table"
* demo ajax by filtering blogentry table on category

we did the same with 'primary', but with tables we can turn on filters
and show that ajax comes for free.
[FILLME]


Templates
---------

*Templates* are specific view that does not depend on a result set. The basic
class `Template` (`cubicweb.common.view`) is derived from the class `View`.

To build a HTML page, a *main template* is used. In general, the template of
identifier `main` is the one (it is not used in case an error is raised or for
the login form for example). This template uses other templates in addition
to the views which depends on the content to generate the HTML page to return.

A *template* is responsible for:

1. executing RQL query of data to render if necessarry
2. identifying the view to use to render data if it is not specified
3. composing the HTML page to return


The default main template (`cubicweb.web.views.basetemplates.TheMainTemplate`)
------------------------------------------------------------------------------

The default main template build the page based on the following pattern:

.. image:: images/main_template_layout.png

The rectangle containing `view.dispathc()` represents the area where the content
view has to be displayed. The others represents sub-templates called to complete
the page. A default implementation of those is provided in 
`cubicweb.views.basetemplates`. You can, of course, overload those sub-templates
to implement your own customization of the HTML page.

We can also control certain aspects of the main template thanks to the following
forms parameters:

* `__notemplate`, if present (whatever the value assigned), only the content view
  is returned
* `__force_display`, if present and its value is not null, no navigation 
  whatever the number of entities to display
* `__method`, if the result set to render contains only one entity and this 
  parameter is set, it refers to a method to call on the entity by passing it
  the dictionnary of the forms parameters, before going the classic way (through
  step 1 and 2 described juste above)

.. include:: B031-views-stdlib.en.txt


XML views, binaries...
----------------------
For the views generating other formats that HTML (an image generated dynamically
for example), and which can not usually be included in the HTML page generated
by the main template (see above), you have to:

* set the atribute `templatable` of the class to `False`
* set, through the attribute `content_type` of the class, the MIME type generated
  by the view to `application/octet-stream`

For the views dedicated to binary content creation (an image dynamically generated
for example), we have to set the attribute `binary` of the class to `True` (which
implies that `templateable == False`, so that the attribute `w` of the view could be
replaced by a binary flow instead of unicode).

(X)HTML tricks to apply
-----------------------

Some web browser (Firefox for example) are not happy with empty `<div>`
(by empty we mean that there is no content in the tag, but there
could be attributes), so we should always use `<div></div>` even if
it is empty and not use `<div/>`.