--- a/doc/book/en/development/devweb/views.rst Wed Sep 16 16:14:48 2009 +0200
+++ b/doc/book/en/development/devweb/views.rst Wed Sep 16 16:52:47 2009 +0200
@@ -34,26 +34,32 @@
* the `category` attribute may be used in the interface to regroup related
objects together
-At instantiation time, the standard `req`, `rset`, and `cursor`
-attributes are added and the `w` attribute will be set at rendering
-time.
+At instantiation time, the standard `req` and `rset` attributes are
+added and the `w` attribute will be set at rendering time.
-A view writes to its output stream thanks to its attribute `w` (`UStreamIO`).
+A view writes to its output stream thanks to its attribute `w` (an
+`UStreamIO`).
The basic interface for views is as follows (remember that the result set has a
tabular structure with rows and columns, hence cells):
-* `dispatch(**context)`, render the view by calling `call` or
+* `render(**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 (default
- implementation calls `cell_call()` on each cell of the result set)
-* `cell_call(row, col, **kwargs)`, call the view for a given cell of a result set
+
+* `call(**kwargs)`, call the view for a complete result set or null
+ (the default implementation calls `cell_call()` on each cell of the
+ result set)
+
+* `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
+ result set. This is actually defined on the AppObject class.
* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, similar to `view` except
the flow is automatically passed in the parameters
@@ -70,8 +76,8 @@
* `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
-* `EmptyRsetView`, view applied to an empty result set
+* `AnyRsetView`, view applicable to any result set
+* `EmptyRsetView`, view applicable to an empty result set
Examples of views class
@@ -103,11 +109,8 @@
__select__ = one_line_rset() & match_search_state('linksearch') & implements('Any')
-Example of a view customization
--------------------------------
-
-[FIXME] XXX Example needs to be rewritten as it shows how to modify cell_call which
-contredicts our advise of not modifying it.
+Example of view customization and creation
+------------------------------------------
We'll show you now an example of a ``primary`` view and how to customize it.
@@ -116,32 +119,26 @@
.. sourcecode:: python
- from cubicweb.view import EntityView
- from cubicweb.selectors import implements
+ from cubicweb.selectors import implements
+ from cubicweb.web.views.primary improt Primaryview
- class BlogEntryPrimaryView(EntityView):
- id = 'primary'
- __select__ =implements('Blog')
+ class BlogEntryPrimaryView(PrimaryView):
+ __select__ = PrimaryView.__select__ & implements('BlogEntry')
- def cell_call(self, row, col):
- entity = self.entity(row, col)
- self.w(u'<h1>%s</h1>' % entity.title)
- self.w(u'<p>published on %s in category %s</p>' % \
- (entity.publish_date.strftime('%Y-%m-%d'), entity.category))
- self.w(u'<p>%s</p>' % entity.text)
+ def render_entity_attributes(self, entity):
+ self.w(u'<p>published on %s</p>' %
+ entity.publish_date.strftime('%Y-%m-%d'))
+ super(BlogEntryPrimaryView, self).render_entity_attributes(entity)
-The above source code defines a new primary view (`line 03`) for
-``BlogEntry`` (`line 05`).
+The above source code defines a new primary view for
+``BlogEntry``. The `id` class attribute is not repeated there since it
+is inherited through the `primary.PrimaryView` class.
-Since views are applied to result sets which can be tables of
-data, we have to recover the entity from its (row,col)-coordinates (`line 08`).
-We will get to this in more detail later.
+The selector for this view chains the selector of the inherited class
+with its own specific criterion.
The view method ``self.w()`` is used to output data. Here `lines
-09-12` output HTML tags and values of the entity's attributes.
-
-When displaying the same blog entry as before, you will notice that the
-page is now looking much nicer. [FIXME: it is not clear to what this refers.]
+08-09` output HTML for the publication date of the entry.
.. image:: ../../images/lax-book.09-new-view-blogentry.en.png
:alt: blog entries now look much nicer
@@ -150,34 +147,74 @@
.. sourcecode:: python
- class BlogPrimaryView(EntityView):
+ from logilab.mtconverter import xml_escape
+ from cubicweb.selectors import implements, one_line_rset
+ from cubicweb.web.views.primary import Primaryview
+
+ class BlogPrimaryView(PrimaryView):
id = 'primary'
- __select__ =implements('Blog')
+ __select__ = PrimaryView.__select__ & implements('Blog')
+ rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s'
+
+ def render_entity_relations(self, entity):
+ rset = self.req.execute(self.rql, {'b' : entity.eid})
+ for entry in rset.entities():
+ self.w(u'<p>%s</p>' % entry.view('inblogcontext'))
+
+ class BlogEntryInBlogView(EntityView):
+ 'inblogcontext'
+ __select__ = implements('BlogEntry')
def cell_call(self, row, col):
- entity = self.entity(row, col)
- self.w(u'<h1>%s</h1>' % entity.title)
- self.w(u'<p>%s</p>' % entity.description)
- rset = self.req.execute('Any E WHERE E entry_of B, B eid "%s"' % entity.eid)
- self.wview('primary', rset)
+ entity = self.rset.get_entity(row, col)
+ self.w(u'<a href="%s" title="%s">%s</a>' %
+ entity.absolute_url(),
+ xml_escape(entity.content[:50]),
+ xml_escape(entity.description))
-In the above source code, `lines 01-08` are similar to the previous
-view we defined. [FIXME: defined where ?]
+This happens in two places. First we override the
+render_entity_relations method of a Blog's primary view. Here we want
+to display our blog entries in a custom way.
-At `line 09`, a simple request is made to build a result set with all
+At `line 10`, a simple request is made to build a result set 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.
+about the schema and infers that such entities have to be of the
+``BlogEntry`` kind and retrieves them (in the prescribed publish_date
+order).
+
+The request returns a selection of data called a result set. Result
+set objects have an .entities() method returning a generator on
+requested entities (going transparently through the `ORM` layer).
+
+At `line 13` the view 'inblogcontext' is applied to each blog entry to
+output HTML. (Note that the 'inblogcontext' view is not defined
+whatsoever in *CubicWeb*. You are absolutely free to define whole view
+families.) We juste arrange to wrap each blogentry output in a 'p'
+html element.
-The request returns a selection of data called a result set. At
-`line 10` the view 'primary' is applied to this result set to output
-HTML.
+Next, we define the 'inblogcontext' view. This is NOT a primary view,
+with its well-defined sections (title, metadata, attribtues,
+relations/boxes). All a basic view has to define is cell_call.
+
+Since views are applied to result sets which can be tables of data, we
+have to recover the entity from its (row,col)-coordinates (`line
+20`). Then we can spit some HTML.
+
+But careful: all strings manipulated in *CubicWeb* are actually
+unicode strings. While web browsers are usually tolerant to incoherent
+encodings they are being served, we should not abuse it. Hence we have
+to properly escape our data. The xml_escape() function has to be used
+to safely fill (X)HTML elements from Python unicode strings.
+
**This is to be compared to interfaces and protocols in object-oriented
languages. Applying a given view called 'a_view' to all the entities
of a result set only requires to have for each entity of this result set,
-an available view called 'a_view' which accepts the entity.**
+an available view called 'a_view' which accepts the entity.
+
+Instead of merely using type based dispatch, we do predicate dispatch
+which quite more powerful**
Assuming we added entries to the blog titled `MyLife`, displaying it
now allows to read its description and all its entries.