cubicweb logo

Table Of Contents

Previous topic

Boxes

Next topic

XML and RSS views

This Page

Table views

This module contains table views, with the following features that may be provided (depending on the used implementation):

  • facets filtering
  • pagination
  • actions menu
  • properly sortable content
  • odd/row/hover line styles

The three main implementation are described below. Each implementation is suitable for a particular case, but they each attempt to display tables that looks similar.

class cubicweb.web.views.tableview.RsetTableView(req=None, rset=None, **kwargs)

This table view accepts any non-empty rset. It uses introspection on the result set to compute column names and the proper way to display the cells.

It is highly configurable and accepts a wealth of options, but take care to check what you’re trying to achieve wouldn’t be a job for the EntityTableView. Basically the question is: does this view should be tied to the result set query’s shape or no? If yes, than you’re fine. If no, you should take a look at the other table implementation.

The following class attributes may be used to control the table:

  • finalvid, a view identifier that should be called on final entities (e.g. attribute values). Default to ‘final’.
  • nonfinalvid, a view identifier that should be called on entities. Default to ‘incontext’.
  • displaycols, if not None, should be a list of rset’s columns to be displayed.
  • headers, if not None, should be a list of headers for the table’s columns. None values in the list will be replaced by computed column names.
  • cellvids, if not None, should be a dictionary with table column index as key and a view identifier as value, telling the view that should be used in the given column.

Notice displaycols, headers and cellvids may be specified at selection time but then the table won’t have pagination and shouldn’t be configured to display the facets filter nor actions (as they wouldn’t behave as expected).

This table class use the RsetTableColRenderer as default column renderer.

class RsetTableColRenderer(cellvid, **kwargs)

Default renderer for RsetTableView.

RsetTableView.default_column_renderer_class

alias of RsetTableColRenderer

RsetTableView.main_var_index()

returns the index of the first non-attribute variable among the RQL selected variables

RsetTableView.table_size

return the number of rows (header excluded) to be displayed

class cubicweb.web.views.tableview.EntityTableView(req=None, rset=None, **kwargs)

This abstract table view is designed to be used with an is_instance() or adaptable predicate, hence doesn’t depend the result set shape as the RsetTableView does.

It will display columns that should be defined using the columns class attribute containing a list of column ids. By default, each column is renderered by EntityTableColRenderer which consider that the column id is an attribute of the table’s main entity (ie the one for which the view is selected).

You may wish to specify MainEntityColRenderer or RelatedEntityColRenderer renderer for a column in the column_renderers dictionary.

class EntityTableColRenderer(renderfunc=None, sortfunc=None, sortable=None, **kwargs)

Default column renderer for EntityTableView.

You may use the entity() method to retrieve the main entity for a given row number.

EntityTableColRenderer.entity(rownum)

Convenience method returning the table’s main entity.

EntityTableColRenderer.render_entity(w, entity)

Sort value if renderfunc nor sortfunc specified at initialization.

This default implementation consider column id is an entity attribute and print its value.

EntityTableColRenderer.entity_sortvalue(entity)

Cell rendering implementation if renderfunc nor sortfunc specified at initialization.

This default implementation consider column id is an entity attribute and return its sort value by calling entity.sortvalue(colid).

class cubicweb.web.views.tableview.MainEntityColRenderer(vid='incontext', addcount=True, **kwargs)

Renderer to be used for the column displaying the ‘main entity’ of a EntityTableView.

By default display it using the ‘incontext’ view. You may specify another view identifier using the vid argument.

If header not specified, it would be built using entity types in the main column.

class cubicweb.web.views.tableview.RelatedEntityColRenderer(getrelated, addcount=False, **kwargs)

Renderer to be used for column displaying an entity related the ‘main entity’ of a EntityTableView.

By default display it using the ‘incontext’ view. You may specify another view identifier using the vid argument.

If header not specified, it would be built by translating the column id.

class cubicweb.web.views.tableview.RelationColRenderer(role='subject', vid='csv', subvid=None, fallbackvid='empty-cell', is_rtype_view=False, **kwargs)

Renderer to be used for column displaying a list of entities related the ‘main entity’ of a EntityTableView. By default, the main entity is considered as the subject of the relation but you may specify otherwise using the role argument.

By default display the related rset using the ‘csv’ view, using ‘outofcontext’ sub-view for each entity. You may specify another view identifier using respectivly the vid and subvid arguments.

If you specify a ‘rtype view’, such as ‘reledit’, you should add a is_rtype_view=True parameter.

If header not specified, it would be built by translating the column id, properly considering role.

EntityTableView.default_column_renderer_class

alias of EntityTableColRenderer

EntityTableView.entity(rownum)

Return the table’s main entity

class cubicweb.web.views.pyviews.PyValTableView(req, pyvalue, headers=None, cssclass=None, header_column_idx=None, **kwargs)

This table view is designed to be used a list of list of unicode values given as a mandatory pyvalue argument. Take care, content is NOT xml-escaped.

It’s configured through the following selection arguments.

If headers is specified, it is expected to be a list of headers to be inserted as first row (in <thead>).

header_column_idx may be used to specify a column index or a set of column indiced where values should be inserted inside <th> tag instead of <td>.

cssclass is the CSS class used on the <table> tag, and default to ‘listing’ (so that the table will look similar to those generated by the table view).

default_column_renderer_class

alias of PyValTableColRenderer

table_size

return the number of rows (header excluded) to be displayed

All those classes are rendered using a layout:

class cubicweb.web.views.tableview.TableLayout(req, view, **kwargs)

The default layout for table. When render is called, this will use the API described on TableMixIn to feed the generated table.

This layout behaviour may be customized using the following attributes / selection arguments:

  • cssclass, a string that should be used as HTML class attribute. Default to “listing”.
  • needs_css, the CSS files that should be used together with this table. Default to (‘cubicweb.tablesorter.css’, ‘cubicweb.tableview.css’).
  • needs_js, the Javascript files that should be used together with this table. Default to (‘jquery.tablesorter.js’,)
  • display_filter, tells if the facets filter should be displayed when possible. Allowed values are: - None, don’t display it - ‘top’, display it above the table - ‘bottom’, display it below the table
  • display_actions, tells if a menu for available actions should be displayed when possible (see two following options). Allowed values are: - None, don’t display it - ‘top’, display it above the table - ‘bottom’, display it below the table
  • hide_filter, when true (the default), facets filter will be hidden by default, with an action in the actions menu allowing to show / hide it.
  • show_all_option, when true, a show all results link will be displayed below the navigation component.
  • add_view_actions, when true, actions returned by view.table_actions() will be included in the actions menu.
  • header_column_idx, if not None, should be a colum index or a set of column index where <th> tags should be generated instead of <td>
initial_load

<wrapped by the cachedproperty decorator> We detect a bit heuristically if we are built for the first time or

from subsequent calls by the form filter or by the pagination hooks.

There is by default only on table layout, using the ‘table_layout’ identifier, that is referenced by table views cubicweb.web.views.tableview.TableMixIn.layout_id. If you want to customize the look and feel of your table, you can either replace the default one by yours, having multiple variants with proper selectors, or change the layout_id identifier of your table to use your table specific implementation.

Notice you can gives options to the layout using a layout_args dictionary on your class.

If you can still find a view that suit your needs, you should take a look at the class below that is the common abstract base class for the three views defined above and implements you own class.

class cubicweb.web.views.tableview.TableMixIn

Abstract mix-in class for layout based tables.

This default implementation’s call method simply delegate to meth:layout_render that will select the renderer whose identifier is given by the layout_id attribute.

Then it provides some default implementation for various parts of the API used by that layout.

Abstract method you will have to override is:

build_column_renderers()

Return a list of column renderers, one for each column to be rendered. Prototype of a column renderer is described below:

class AbstractColumnRenderer(header=None, addcount=False, trheader=True, escapeheader=True, sortable=True)

Abstract base class for column renderer. Interface of a column renderer follows:

AbstractColumnRenderer.bind(view, colid)

Bind the column renderer to its view. This is where _cw, view, colid are set and the method to override if you want to add more view/request depending attributes on your column render.

AbstractColumnRenderer.render_header(w)

Write label for the specified column by calling w().

AbstractColumnRenderer.render_cell(w, rownum)

Write value for the specified cell by calling w().

Parameters:rownum – the row number in the table
AbstractColumnRenderer.sortvalue(_rownum)

Return typed value to be used for sorting on the specified column.

Parameters:rownum – the row number in the table

Attributes on this base class are:

Attr :header, the column header. If None, default to _(colid)
Attr :addcount, if True, add the table size in parenthezis beside the header
Attr :trheader, should the header be translated
Attr :escapeheader, should the header be xml_escaped
Attr :sortable, tell if the column is sortable
Attr :view, the table view
Attr :_cw, the request object
Attr :colid, the column identifier
Attr :attributes, dictionary of attributes to put on the HTML tag when the cell is rendered

You may also want to overridde:

TableMixIn.table_size

Return the number of rows (header excluded) to be displayed.

By default return the number of rows in the view’s result set. If your table isn’t reult set based, override this method.

The has_headers boolean attribute tells if the table has some headers to be displayed. Default to True.

TableMixIn.build_column_renderers()

Return a list of column renderers, one for each column to be rendered. Prototype of a column renderer is described below:

class AbstractColumnRenderer(header=None, addcount=False, trheader=True, escapeheader=True, sortable=True)

Abstract base class for column renderer. Interface of a column renderer follows:

AbstractColumnRenderer.bind(view, colid)

Bind the column renderer to its view. This is where _cw, view, colid are set and the method to override if you want to add more view/request depending attributes on your column render.

AbstractColumnRenderer.render_header(w)

Write label for the specified column by calling w().

AbstractColumnRenderer.render_cell(w, rownum)

Write value for the specified cell by calling w().

Parameters:rownum – the row number in the table
AbstractColumnRenderer.sortvalue(_rownum)

Return typed value to be used for sorting on the specified column.

Parameters:rownum – the row number in the table

Attributes on this base class are:

Attr :header, the column header. If None, default to _(colid)
Attr :addcount, if True, add the table size in parenthezis beside the header
Attr :trheader, should the header be translated
Attr :escapeheader, should the header be xml_escaped
Attr :sortable, tell if the column is sortable
Attr :view, the table view
Attr :_cw, the request object
Attr :colid, the column identifier
Attr :attributes, dictionary of attributes to put on the HTML tag when the cell is rendered
TableMixIn.column_renderer(colid, *args, **kwargs)

Return a column renderer for column of the given id.

TableMixIn.domid

<wrapped by the cachedproperty decorator>

TableMixIn.table_actions()

Return a list of actions (Link) that match the view’s result set, and return those in the ‘mainactions’ category.

TableMixIn.table_size

Return the number of rows (header excluded) to be displayed.

By default return the number of rows in the view’s result set. If your table isn’t reult set based, override this method.

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=worpackage_cell,
           sortfunc=worpackage_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).