Warning

Starting from CubicWeb version 4.0 all code related to generating html views has been moved to the Cube cubicweb_web.

If you want to migrate a project from 3.38 to 4.* while still using all the html views you need to both install the cubicweb_web cube AND add it to your dependencies and run add_cube('web').

cubicweb_web can be installed from pypi this way:

pip install cubicweb_web

We don’t plan to maintain the features in cubicweb_web in the long run; we are moving to a full javascript frontend using both cubicweb_api (which exposes a HTTP API) and @cubicweb/client as a frontend javascript toolkit.

In the long run cubicweb_api will be merged inside of CubicWeb.

Table views#

Example#

Let us take an example from the timesheet cube:

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:

../../../_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:

../../../_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 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 EntityTableView, as below.

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 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 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#

EntityTableView and 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).