Defines the classes AbstractTable and VirtualTable.

(This module's source code is available here.)


AbstractTable(*args, **kw)

An AbstractTable is the definition of a tabular data view, usually displayed in a Grid (but it's up to the user interface to decide how to implement this).

ButtonsTable(*args, **kw)

An abstract VirtualTable with only one column and whose rows are action buttons.



For every table we create one "handle" per renderer.

VentilatedColumns(*args, **kw)

A mixin for tables that have a series of automatically generated columns

VentilatingTable(*args, **kw)

VirtualTable(*args, **kw)

An AbstractTable that works on an volatile (non persistent) list of rows.



class lino.core.tables.TableHandle(actor)

Bases: object

For every table we create one "handle" per renderer.

See also lino.core.actors.Actor.setup_handle() which is called during startup and sets more attributes.

class lino.core.tables.AbstractTable(*args, **kw)

Bases: lino.core.actors.Actor

An AbstractTable is the definition of a tabular data view, usually displayed in a Grid (but it's up to the user interface to decide how to implement this).

Base class for Table and VirtualTable.

hide_zero_rows = False

Set this to True if you want to remove rows which contain no values when rendering this table as plain HTML. This is ignored when rendered as ExtJS.

column_names = '*'

A string that describes the list of columns of this table.

Lino will automatically create a lino.core.layouts.ColumnsLayout from this. This string must not contain any newline characters because a ColumnsLayout's main panel descriptor must be horizontal.

Default value is '*'. Where all columns are included. This wildcard character means "all columns which have not been named explicitly can be selected by the user and inserted at this point". It can be combined with explicitly specified names.

For example:

column_names = "name owner * date"

specifies that name and owner come first, followed by inserted columns and finally by date.

If '*' is not present in the string only explicitly named columns will be available.

See also setup_column() and get_column_names().

tablet_columns = {}

The columns that must remain visible when this table is rendered on a tablet device.

mobile_columns = {}

The columns that must remain visible when this table is rendered on a mobile device.

popin_columns = {}

The columns that must pop in below the first column if there is no space to render them on the device.

If None: All columns not listed in mobile_columns nor Tablet_columns will not pop-in.

start_at_bottom = False

Set this to True if you want your table to start at the bottom. Unlike reverse ordering, the rows remain in their natural order, but when we open a grid on this table, we want it to start on the last page.

Use cases would be lino_xl.lib.sales.InvoicesByJournal and lino_xl.lib.ledger.InvoicesByJournal but the result is not yet satisfying.

group_by = None

A list of field names that define the groups of rows in this table. Each group can have her own header and/or total lines.

custom_groups = []

Used internally to store groups defined by this Table.

master_field = None

For internal use. Automatically set to the field descriptor of the master_key.

get_data_rows = None

Maybe deprecated. Use get_request_queryset() instead.

Virtual tables must define this method, model-based tables may define it. e.g. in case they need local filtering.

This will be called with a lino.core.requests.TableRequest object and is expected to return or yield the list of "rows":

def get_data_rows(self, ar):
    yield somerow
preview_limit = 15

The maximum number of rows to fetch when this table is being displayed in "preview mode", i.e. (1) as a slave table in a detail window or (2) as a dashboard item (get_dashboard_items) in admin_main.html.

The default value for this is the preview_limit class attribute of your Site, which itself has a hard-coded default value of 15 and which you can override in your

If you set this to 0, preview requests for this table will request all rows. Since preview tables usually have no paging toolbar, that's theoretically what we want (but can lead to waste of performance if there are many rows).

In React if set to 0 the paging toolbar which usually is present in the detail view, will be removed, as it has no use, as all rows wil be displayed.

Test case and description in the tested docs of Lino Così.

row_height = 1

Number of text rows per data row.

variable_row_height = False

Set this to True if you want each row to get the height that it needs.

auto_fit_column_widths = True

Set this to True if you want to have the column widths adjusted to always fill the available width. This implies that there will be no horizontal scrollbar.

active_fields = frozenset({})

A list of field names that are "active". Value and inheritance as for hidden_columns.

When a field is "active", this means only that it will cause an immediate "background" save and refresh of the detail window when their value was changed. The true "activity" (i.e. other fields being updated according to the value of an active field) is defined in the model's full_clean and FOO_changed methods.

Note that active fields are active only in a detail window, not in an insert window. That's because there they would lead to the unexpected behaviour of closing the window.

hidden_columns = frozenset({})

If given, this is specifies the data elements that should be hidden by default when rendering this table. Example:

hidden_columns = "long_name expected_date"

Value : Application code should specify this as a single string containing a space-separated list of field names. Lino will automatically resolve this during server startup using lino.core.utils.fields_list(). The runtime value of this attribute is a set of strings, each one the name of a data element. Defaults to an empty set.

Inheritance : Note that this can be specified either on a Model or on a Table. Lino will make a union of both.

page_length = 20

Number of rows to display per page. Used to control the height of a combobox of a ForeignKey pointing to this model

cell_edit = True

True (default) to use ExtJS CellSelectionModel, False to use RowSelectionModel. When True, the users cannot select multiple rows. When False, the users cannot edit individual cells using the F2 key..

show_detail_navigator = True

Whether a Detail view on a row of this table should have a navigator.

typo_check = True

True means that Lino shoud issue a warning if a subclass defines any attribute that did not exist in the base class. Usually such a warning means that there is something wrong.

display_mode = 'grid'

How to display this table when it is a slave in a detail window. Must be one of the following values:

  • 'grid' (default) to render as a grid.

  • 'summary' to render a summary in a HtmlBoxPanel.

  • 'html' to render plain html a HtmlBoxPanel.

  • 'cards' to render a defined layout as a grid of cards (react only)

  • 'list' to render a defined layout as a list of cards (react only

See Table summaries.

max_render_depth = 2

Used to limit the rendering of slave card views.

hide_if_empty = False

In a detail view if a slave table is empty, it's element will be hidden

stay_in_grid = False

Set this to True if Lino should prefer grid mode and not open a detail window on a newly created record. SubmitDetail closes the window when this is True.

Usage example LanguageKnowledgesByPerson.

no_phantom_row = False

Suppress a phantom row in situations where Lino would otherwise add one.

Used for lino_xl.lib.ledger.ByJournal where a phantom row is disturbing.

TODO: Actually this option would not be necessary if the AJAX call sent by a grid panel would include an option which says whether it is main item or not.

grid_configs = []

Will be filled during lino.core.table.Table.do_setup().

order_by = None

If specified, this must be a tuple or list of field names which will be passed to Django's order_by method in order to sort the rows of the queryset.

filter = None

If specified, this must be a models.Q object (not a dict of (fieldname -> value) pairs) which will be passed to Django's filter method.

Note that if you allow a user to insert rows into a filtered table, you should make sure that new records satisfy your filter condition, otherwise you can get surprising behaviour if the user creates a new row.

If your filter consists of simple static values on some known field, then you might prefer to use known_values instead because this will add automatic behaviour.

One advantage of filter over known_values is that this can use the full range of Django's field lookup methods

exclude = None

If specified, this must be dict which will be passed to Django's exclude method on the queryset.

extra = None


extra = dict(select=dict(lower_name='lower(name)'))
# (or if you prefer:)
# extra = {'select':{'lower_name':'lower(name)'},'order_by'=['lower_name']}

List of SQL functions and which RDBMS supports them:

hide_sums = False

Set this to True if you don't want Lino to display sums in a table view.

use_paging = False

Set this to True in Extjs6 to not use a Buffered Store, and use a JsonStore with paging instead.

drag_drop_sequenced_field = None

Extjs6 only Enables drag and drop reordering for a table. Set to the field name that is used to track the order. Only used in lino.mixins.sequenced.Sequenced. Field name seqno

If True , when the grid opens, the initial keyboard focus will be in the quick search field.

classmethod parse_req(request, rqdata, **kw)

This is called when an incoming web request on this actor is being parsed.

If you override parse_req(), then keep in mind that it will be called before Lino checks the requirements. For example the user may be AnonymousUser even if the requirements won't let it be executed. ar.subst_user.user_type may be None, e.g. when called from find_appointment in welfare.pcsw.Clients.

classmethod get_row_by_pk(ar, pk)

dbtables.Table overrides this.

classmethod get_column_names(ar)

Dynamic tables can subclass this method and return a value for column_names which depends on the request.

classmethod get_filter_kw(ar, **kw)

Return a dict with the "master keywords" for this table and a given master_instance.

For example, if you have two models Book and Author, and a foreign key which points to the author of the book, and a table BooksByAuthor having master_key set to 'author', then get_filter_kw would return a dict {'author': <PK>} where PK is the primary key of the action request's master_instance.

Another example is, where blog entries are not directly linked to a session, but in the detail of a session we want to display a table of related blog entries.

lino_xl.lib.households.SiblingsByPerson Household members are not directly linked to a Person, but usually a Person is member of exactly one household, and in the Detail of a Person we want to display the members of that household.

classmethod request(master_instance=None, **kw)

Return a new TableRequest on this table.

If this is a slave table, the master_instance can be specified as optional first positional argument.

classmethod run_action_from_console(pk=None, an=None)

Not yet stable. Used by To be combined with the show management command.

classmethod add_quick_search_filter(data, search_text)

Add a filter to the given data iterator in order to apply a quick search for the given search_text.

class lino.core.tables.VirtualTable(*args, **kw)

Bases: lino.core.tables.AbstractTable

An AbstractTable that works on an volatile (non persistent) list of rows.

By nature it cannot have database fields, only virtual fields.

Subclasses must define a get_data_rows() method.

class lino.core.tables.VentilatedColumns(*args, **kw)

Bases: lino.core.tables.VirtualTable

A mixin for tables that have a series of automatically generated columns

column_names_template = ''

The template to use for column_names. It should contain a string {vcolumns} which will be replaced by a space-separated list of the column names given by get_ventilated_columns().

class lino.core.tables.ButtonsTable(*args, **kw)

Bases: lino.core.tables.VirtualTable

An abstract VirtualTable with only one column and whose rows are action buttons.

Subclasses must implement get_data_rows to yield action buttons.

Usage example lino_welfare.modlib.reception.models.FindDateByClientTable.