doc/book/devweb/views/table.rst
author Denis Laxalde <denis.laxalde@logilab.fr>
Fri, 05 Apr 2019 17:58:19 +0200
changeset 12567 26744ad37953
parent 10829 550c2d27339f
permissions -rw-r--r--
Drop python2 support This mostly consists in removing the dependency on "six" and updating the code to use only Python3 idioms. Notice that we previously used TemporaryDirectory from cubicweb.devtools.testlib for compatibility with Python2. We now directly import it from tempfile.

Table views
-----------

.. automodule:: cubicweb.web.views.tableview

Example
```````

Let us take an example from the timesheet cube:

.. sourcecode:: python

    class ActivityResourcesTable(EntityView):
        __regid__ = 'activity.resources.table'
        __select__ = is_instance('Activity')

        def call(self, showresource=True):
            eids = ','.join(str(row[0]) for row in self.cw_rset)
            rql = ('Any R,D,DUR,WO,DESCR,S,A, SN,RT,WT ORDERBY D DESC '
                   'WHERE '
                   '   A is Activity, A done_by R, R title RT, '
                   '   A diem D, A duration DUR, '
                   '   A done_for WO, WO title WT, '
                   '   A description DESCR, A in_state S, S name SN, '
                   '   A eid IN (%s)' % eids)
            rset = self._cw.execute(rql)
            self.wview('resource.table', rset, 'null')

    class ResourcesTable(RsetTableView):
        __regid__ = 'resource.table'
        # notice you may wish a stricter selector to check rql's shape
        __select__ = is_instance('Resource')
        # my table headers
        headers  = ['Resource', 'diem', 'duration', 'workpackage', 'description', 'state']
        # I want a table where attributes are editable (reledit inside)
        finalvid = 'editable-final'

        cellvids = {3: 'editable-final'}
        # display facets and actions with a menu
        layout_args = {'display_filter': 'top',
                       'add_view_actions': None}

To obtain an editable table, you may specify the 'editable-table' view identifier
using some of `cellvids`, `finalvid` or `nonfinalvid`.

The previous example results in:

.. image:: ../../../images/views-table-shadow.png

In order to activate table filter mechanism, the `display_filter` option is given
as a layout argument. A small arrow will be displayed at the table's top right
corner. Clicking on `show filter form` action, will display the filter form as
below:

.. image:: ../../../images/views-table-filter-shadow.png

By the same way, you can display additional actions for the selected entities
by setting `add_view_actions` layout option to `True`. This will add actions
returned by the view's :meth:`~cubicweb.web.views.tableview.TableMixIn.table_actions`.

You can notice that all columns of the result set are not displayed. This is
because of given `headers`, implying to display only columns from 0 to
len(headers).

Also Notice that the `ResourcesTable` view relies on a particular rql shape
(which is not ensured by the way, the only checked thing is that the result set
contains instance of the `Resource` type). That usually implies that you can't
use this view for user specific queries (e.g. generated by facets or typed
manually).


So another option would be to write this view using
:class:`~cubicweb.web.views.tableview.EntityTableView`, as below.

.. sourcecode:: python

    class ResourcesTable(EntityTableView):
        __regid__ = 'resource.table'
        __select__ = is_instance('Resource')
        # table columns definition
        columns  = ['resource', 'diem', 'duration', 'workpackage', 'description', 'in_state']
        # I want a table where attributes are editable (reledit inside)
        finalvid = 'editable-final'
        # display facets and actions with a menu
        layout_args = {'display_filter': 'top',
                       'add_view_actions': None}

        def workpackage_cell(entity):
            activity = entity.reverse_done_in[0]
            activity.view('reledit', rtype='done_for', role='subject', w=w)
        def workpackage_sortvalue(entity):
            activity = entity.reverse_done_in[0]
            return activity.done_for[0].sortvalue()

        column_renderers = {
            'resource': MainEntityColRenderer(),
            'workpackage': EntityTableColRenderer(
               header='Workpackage',
               renderfunc=workpackage_cell,
               sortfunc=workpackage_sortvalue,),
            'in_state': EntityTableColRenderer(
               renderfunc=lambda w,x: w(x.cw_adapt_to('IWorkflowable').printable_state),
               sortfunc=lambda x: x.cw_adapt_to('IWorkflowable').printable_state),
         }

Notice the following point:

* `cell_<column>(w, entity)` will be searched for rendering the content of a
  cell. If not found, `column` is expected to be an attribute of `entity`.

* `cell_sortvalue_<column>(entity)` should return a typed value to use for
  javascript sorting or None for not sortable columns (the default).

* The :func:`etable_entity_sortvalue` decorator will set a 'sortvalue' function
  for the column containing the main entity (the one given as argument to all
  methods), which will call `entity.sortvalue()`.

* You can set a column header using the :func:`etable_header_title` decorator.
  This header will be translated. If it's not an already existing msgid, think
  to mark it using `_()` (the example supposes headers are schema defined msgid).


Pro/cons of each approach
`````````````````````````
:class:`EntityTableView` and :class:`RsetableView` provides basically the same
set of features, though they don't share the same properties. Let's try to sum
up pro and cons of each class.

* `EntityTableView` view is:

  - more verbose, but usually easier to understand

  - easily extended (easy to add/remove columns for instance)

  - doesn't rely on a particular rset shape. Simply give it a title and will be
    listed in the 'possible views' box if any.

* `RsetTableView` view is:

  - hard to beat to display barely a result set, or for cases where some of
    `headers`, `displaycols` or `cellvids` could be defined to enhance the table
    while you don't care about e.g. pagination or facets.

  - hardly extensible, as you usually have to change places where the view is
    called to modify the RQL (hence the view's result set shape).