diff -r 0d953f0b41c4 -r 6536ee4f37f7 doc/book/en/development/devweb/views.rst --- 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'

%s

' % entity.title) - self.w(u'

published on %s in category %s

' % \ - (entity.publish_date.strftime('%Y-%m-%d'), entity.category)) - self.w(u'

%s

' % entity.text) + def render_entity_attributes(self, entity): + self.w(u'

published on %s

' % + 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'

%s

' % entry.view('inblogcontext')) + + class BlogEntryInBlogView(EntityView): + 'inblogcontext' + __select__ = implements('BlogEntry') def cell_call(self, row, col): - entity = self.entity(row, col) - self.w(u'

%s

' % entity.title) - self.w(u'

%s

' % 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'%s' % + 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.