doc/book/en/development/devweb/views.rst
branchstable
changeset 3258 6536ee4f37f7
parent 2544 282261b26774
child 3293 69c0ba095536
--- 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.