doc/book/fr/05-define-views.fr.txt
author Nicolas Chauvat <nicolas.chauvat@logilab.fr>
Wed, 12 Aug 2009 19:09:32 +0200
changeset 2789 39712da6f397
parent 93 9c919a47e140
permissions -rw-r--r--
R propagate deprecation of AppObject.entity()

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

.. _DefinitionVues:

Définition de vues
==================

Les classes de base des vues
----------------------------

La class `View` (`cubicweb.common.view`)
````````````````````````````````````````
Un vue écrit dans son flux de sortie via son attribut `w` (`UStreamIO`).

L'interface de base des vues est la suivante :

* `dispatch(**context)`, appelle ("rend") la vue en appellent `call` ou
  `cell_call` en fonction des arguments passé
* `call(**kwargs)`, appelle la vue pour un result set complet ou nul
* `cell_call(row, col, **kwargs)`, appelle la vue pour une cellule donnée d'un
  result set
* `url()`, retourne l'url permettant d'obtenir cette vue avec le result set en
  cours
* `view(__vid, rset, __fallback_vid=None, **kwargs)`, appelle la vue
  d'identificant `__vid` sur le result set donné. Il est possible de données un
  identificant de vue de "fallback" qui sera utilisé si la vue demandée n'est
  pas applicable au result set

* `wview(__vid, rset, __fallback_vid=None, **kwargs)`, pareil que `view` mais
  passe automatiquement le flux en argument

* `html_headers()`, retourne une liste d'en-tête HTML à placer par le template
  principal

* `page_title()`, retourne le titre à utiliser dans l'en tête HTML `title`

* `creator(eid)`, retourne l'eid et le login du créateur de l'entité ayant
  l'eid passé en argument

Autres classes de base :

* `EntityView`, vue s'appliquant à aux lignes ou cellule contenant une entité
  (eg un eid)
* `StartupView`, vue de départ n'ayant pas besoin de result set
* `AnyRsetView`, vue s'appliquant à n'importe quelle result set

Le mecanisme de selection de vues
---------------------------------

Pour un identifiant de vue donne, plusieurs vues peuvent etre definies.
`CubicWeb` utilise un selecteur qui permet de calculer un score et d'identifier
la vue la plus appropriee a appliquer dans le contexte. La librairie du selecteur
se trouve dans ``cubicweb.common.selector`` et une librairie des methodes utilisees
pour calculer les scores est dans ``cubicweb.vregistry.vreq``.

[FROM-LAX-BOOK]

Tip: when modifying views, you do not need to restart the local
server. Just save the file in your editor and reload the page in your
browser to see the changes.

With `LAX`, views are defined by Python classes. 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

`LAX` provides a lot of standard views, for a complete list, you
will have to read the code in directory ``ginco/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.rset.get_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.fr.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.rset.get_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.fr.png
   :alt: a blog and all its entries

**Before we move forward, remember that the selection/view principle is
at the core of `LAX`. 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 ginco.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]

Les templates ou patron
-----------------------

Les patrons (ou *template*) sont des cas particulier de vue ne dépendant a
priori pas d'un result set. La classe de base `Template` (`cubicweb.common.view`)
est une classe dérivée de la classe `View`.

Pour construire une page HTML, un *template principal* est utilisé. Généralement
celui possédant l'identifiant 'main' est utilisé (ce n'est pas le cas lors
d'erreur dans celui-ci ou pour le formulaire de login par exemple). Ce patron
utilise d'autres patrons en plus des vues dépendants du contenu pour générer la
page à renvoyer.

C'est ce template qui est chargé :

1. d'éxécuter la requête RQL des données à afficher le cas échéant
2. éventuellement de déterminer la vue à utiliser pour l'afficher si non
   spécifiée
3. de composer la page à retourner


Le patron principal par défaut (`cubicweb.web.views.basetemplates.TheMainTemplate`)
-----------------------------------------------------------------------------------

Le template principal par défaut construit la page selon la décomposition
suivante :

.. image:: images/main_template_layout.png

Le rectancle contenant le `view.dispatch()` représente l'emplacement où est
inséré la vue de contenu à afficher. Les autres représentent des sous-templates
appelé pour construire la page. Les implémentations par défaut de tout ces
templates sont dans le module `cubicweb.web.views.basetemplates`. Vous pouvez
évidemment surcharger l'un des sous-templates pour modifier l'aspect visuel
d'une partie désirée de la page.

On peut également contrôler certains comportements du template principal à
l'aide des paramètres de formulaire suivante :

* `__notemplate`, si présente (quelque soit la valeur associée), seule la vue de
  contenu est renvoyée
* `__force_display`, si présente et contient une valeur non nulle, pas de
  navigation quelque soit le nombre d'entités à afficher
* `__method`, si le result set à afficher ne contient qu'une entité et que ce
  paramètre est spécifié, celui-ci désigne une méthode à appeler sur l'entité
  en lui donnant en argument le dictionnaire des paramètres de formulaire, avant
  de reprendre le comportement classique (s'insère entre les étapes 1. et
  2. décrites ci-dessus)


.. include:: 05-01-views-stdlib.fr.txt


Vues xml, binaires...
---------------------
Pour les vues générants autre que du html  (une image générée dynamiquement par
exemple), et qui ne peuvent donc généralement pas être incluse dans la page
HTML générée par le template principal (voir ci-dessus), il faut :

* placer l'attribut `templatable` de la classe à `False`
* indiquer via l'attribut `content_type` de la classe le type MIME généré par la
  vue 'application/octet-stream'

Pour les vues générants un contenu binaire (une image générée dynamiquement par
exemple), il faut également placer l'attribut `binary` de la classe à `True` (ce
qui implique `templatable == False` afin que l'attribut `w` de la vue soit
remplacé par un flux binaire plutôt que unicode.


Quelques trucs (X)HTML à respecter
----------------------------------
Certains navigateurs (dont firefox) n'aime pas les `<div>` vides (par vide
j'entend sans contenu dans la balise, il peut y avoir des attributs), faut
toujours mettre `<div></div>` même s'il n'y a rien dedans, et non `<div/>`.