Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More
How to represent a database row¶
Lino offers a number of ways to customize how a database row is to be presented to the end user.
Note
This page needs more work.
Side note: Code snippets (lines starting with >>>
) in this document get
tested as part of our development workflow. The following
initialization snippet tells you which demo project is being used in
this document.
>>> import lino
>>> lino.startup('lino_book.projects.min1.settings')
>>> from django.utils import translation
>>> from lino.api.doctest import *
>>> from django.db.models import Q
Summary items, paragraphs and pages¶
Lino differentiates three levels of representation for a database row: “summary item”, “paragraph” and “page”.
Customize the summary item¶
When you don’t give any instructions at all, Lino uses the __str__()
method to represent a database row as a summary item.
More precisely, Lino calls Model.as_str()
when a context is known.
A context-sensitive variant of __str__()
.
The plain-text representation of a
database row can vary depending on whether certain elements are considered
“obvious”.
For example when you are displaying a list of the periods in a given fiscal year, it would be redundant to print the year for each of them. So the StoredPeriod.as_str() method does not mention the year when ar.is_obvious_field(“year”) returns True or when it is part of the current fiscal year.
Other examples are the country of a city.
The Model.as_summary_item()
method is often customized on models for which
there is no detail window. For example lists.Member, contacts.Role or
topics.Tag. These models have in common that they represent a simple relation.
- class lino.core.model.Model
- __str__(self)¶
Return a translatable text that describes this database row.
- as_str(self, ar)¶
Return a translatable text that describes this database row. Unlike
__str__()
this method gets an action request when it is called, so it knows the context.
- get_str_words(self, ar)¶
Yield a series of words that describe this database row in plain text.
Called by the default implementation of
as_str()
, which then joins all words by a space (" "
) into a single string.
- as_summary_item(self, ar, text=None, **kwargs)¶
Return a HTML element that represents this database row in a data window in display mode “summary”.
Default implementation calls
as_str()
and passes this astext
toar.obj2html
.Examples see The summary display mode.
- class lino.core.actors.Actor
- row_template¶
A format template to make a plain text description of a row of this table in.
Default value is None. If this is a str the default implementation of
Model.as_str()
will parse by calling itsformat()
method with one keyword argumentrow
, which refers to the database row being represented.Example:
class RatingsByResponse(ChallengeRatings): ... row_template = '{row.rating}/{row.challenge.max_rating} {row.challenge.skill}'
Represent a row depending on the context¶
The Model
class has three methods used to represent a database
row.
as_summary_item
represents it as a summary item.as_paragraph
represents it as a single paragraph.as_page
represents it as a whole web page.
Both methods require an action request as argument, and the result may vary depending on this action request. For example a partner model of a given application may want to also show the city of a partner unless city is an obvious field:
def as_summary_item(self, ar, *text, **kwargs):
s = ar.obj2htmls(self)
if self.city and not ar.is_obvious_field("city"):
s = format_html("{} from {}", s, ar.obj2htmls(self.city))
return s
- obvious field¶
A field for which the value is the same for alll rows of a table.
Default implementation returns ar.obj2htmls(self)
or (when ar
is None)
str(self)
.
The returned HTML string should contain a single paragraph and must not include
any surrounding <li>
or <p>
or <td>
tag (these will be added by the
caller as needed).
>>> ar = rt.login("robin")
>>> hans = contacts.Person.objects.all().first()
>>> hans.as_paragraph(ar)
'<a href="…">Mr Hans Altenberg</a> (Aachener Straße, 4700 Eupen)'
>>> robin = ar.get_user()
>>> robin.as_paragraph(ar)
'<a href="…">Robin Rood</a>'
Lino usually doesn’t call this method directly, it mostly calls the
row_as_paragraph
method of a data table, and this method, by default, calls our model
method. The following two calls give the same results as the former ones:
>>> contacts.Persons.row_as_paragraph(ar, hans)
'<a href="…">Mr Hans Altenberg</a> (Aachener Straße, 4700 Eupen)'
>>> users.Users.row_as_paragraph(ar, robin)
'<a href="…">Robin Rood</a>'
You can customize this by overriding either the model or the data table.
For example, lino.modlib.users.UsersOverview
customizes the data table:
@classmethod
def row_as_paragraph(cls, ar, self):
pv = dict(username=self.username)
if settings.SITE.is_demo_site:
pv.update(password="1234")
btn = rt.models.about.About.get_action_by_name("sign_in")
btn = btn.request(
action_param_values=pv, renderer=settings.SITE.kernel.default_renderer
)
btn = btn.ar2button(label=self.username)
items = [tostring(btn), " : ", str(self), ", ", str(self.user_type)]
if self.language:
items += [
", ",
"<strong>{}</strong>".format(
settings.SITE.LANGUAGE_DICT.get(self.language)
),
]
return mark_safe("".join(items))
That’s why we get:
>>> users.UsersOverview.row_as_paragraph(ar, robin)
...
'<a href="javascript:Lino.about.About.sign_in.run(null,{
"base_params": { }, "field_values": { "password":
"", "username": "robin" }, "record_id":
null })" style="text-decoration:none">robin</a> : Robin Rood, 900
(Administrator), <strong>English</strong>'
There is also a shortcut method row_as_paragraph
on an action request.
Customize the title of an actor¶
- class lino.core.actors.Actor
- label¶
The text to appear e.g. on a button that will call the default action of an actor. This attribute is not inherited to subclasses. If this is None (the default value), Lino will call
get_actor_label()
.
- get_title(self, ar)¶
Return the title of this actor for the given action request ar.
The default implementation calls
get_title_base()
andget_title_tags()
and returns a string of type BASE [ (TAG, TAG…)].Override this if your table’s title should mention for example filter conditions. See also
Table.get_title
.
- get_actor_label(self)¶
Return the label of this actor.
- title¶
The text to appear e.g. as window title when the actor’s default action has been called. If this is not set, Lino will use the
label
as title.
- button_text¶
The text to appear on buttons of a ShowSlaveTable action for this actor.
- get_title_base(self, ar)¶
Return the base part of the title. This should be a translatable string. This is called by
get_title()
to construct the actual title.It is also called by
lino.core.dashboard.DashboardItem.render_request()
- get_title_tags(self, ar)¶
Yield a list of translatable strings to be added to the base part of the title. This is called by
get_title()
to construct the actual title.
Miscellaneous¶
- class lino.core.model.Model
- as_paragraph(self, ar)¶
Return a safe HTML string that represents this database row as a paragraph.
See Represent a row depending on the context.
This is called by the default implementation of
lino.core.actors.Actor.row_as_paragraph()
.
- preferred_foreignkey_width = None
The default preferred width (in characters) of widgets that display a foreign key to this model.
If not specified, the default default preferred_width for ForeignKey fields is 20.
- set_widget_options(self, name, **options)¶
Set default values for the widget options of a given element.
Customize actor methods and attributes¶
See More about layouts. More about layouts.
When you define a detail_layout
, you probably also want to define an
insert_layout
.
When a table has no insert_layout
, it won’t have any (+) button
() to create a new row via a dialog window, but users can still
create rows by writing into the phantom row. Example of this is
lino_xl.lib.courses.Topics
which has a detail layout with slave
tables, but the model itself has only two fields (id and name) and it makes
no sense to have an insert window.
- class lino.core.actors.Actor
- detail_layout¶
Define the layout to use for the detail window. Actors with
detail_layout
will get a show_detail action.
- insert_layout¶
Define the form layout to use for the insert window.
Miscellaneous¶
- class lino.core.actors.Actor
- classmethod get_row_classes(self, ar)¶
If a method of this name is defined on an actor, then it must be a class method which takes an
ar
as single argument and returns either None or a string “red”, “green” or “blue” (todo: add more colors and styles). Example:@classmethod def get_row_classes(cls,obj,ar): if obj.client_state == ClientStates.newcomer: return 'green'
Defining this method will cause an additional special RowClassStoreField field in the
lino.core.Store
objects of this actor.
- details_of_master_template¶
Used to build the title of a request on this table when it is a slave table (i.e.
master
is not None). The default value is defined as follows:details_of_master_template = _("%(details)s of %(master)s")
- display_mode¶
The default display mode to use when rendering this table.
- window_size = None
Set this to a tuple of (height, width) to have this actor display in a modal non-maximized window.
height must be either an integer expressing a number of rows or the string “auto”. If it is auto, then the window should not contain any v-flexible component.
width must be either an integer expressing a number of rows or a string of style “90%”.
Note that a relative width will be converted to a number of pixels when the window is rendered for the first time. That is, if you close the window, resize your browser window and reopen the same window, you will get the old size.
- insert_layout_width = 60
When specifying an
insert_layout
using a simple a multline string, then Lino will instantiate a FormPanel with this width.
- hide_window_title = False
This is set to True e.h. in home pages (e.g.
lino_welfare.modlib.pcsw.models.Home
).
- hide_headers = False
Set this to True in order to hide the column headers.
This is ignored when the table is rendered in an ExtJS grid.
- hide_top_toolbar = False
Whether a Detail Window should have navigation buttons, a “New” and a “Delete” buttons. In ExtJS UI also influences the title of a Detail Window to specify only the current element without prefixing the Tables’s title.
If used in a grid view in React will remove the top toolbar and selection tools.
This option is True in
lino.models.SiteConfigs
,lino_welfare.pcsw.models.Home
,lino.modlib.users.desktop.MySettings
,lino_xl.cal.CalenderView
.
- simple_slavegrid_header¶
Whether to simplify the slave grid in a detail.
This is used only in
lino.modlib.comments.RepliesByComment
and should probably be replaced by something else.
- preview_limit¶
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
) inadmin_main.html
.The default value for this is the
preview_limit
class attribute of yourSite
, which itself has a hard-coded default value of 15 and which you can override in yoursettings.py
.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). When this is 0, there will be no no paginator.
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 will be displayed.
Test case and description in the tested docs of Lino Così.
For non-table actors this is always None.
- help_text = None
A help text that shortly explains what the default action of this actor does. In a graphical user interface this will be rendered as a tooltip text.
If this is not given by the code, Lino will potentially set it at startup when loading the
help_texts.py
files.
- summary_row(cls, ar, obj, **kw)¶
Return a HTML representation of the given data row obj for usage in a summary panel.
The default implementation calls
lino.core.model.Model.summary_row()
.
React-specific actor attributes¶
- class lino.core.actors.Actor
- use_detail_param_panel¶
Whether to show parameter panel in a detail view.
- use_detail_params_value¶
Whether to use the parent’s parameter values in grid
- react_big_search¶
Whether the quick search field should be rendered on a line on its own.
This is set to True only for contacts.Persons in Lino Amici and should probably be replaced by something else.
- max_render_depth¶
This is not used.
- paginator_template¶
Paginator elements can be customized using the template property using the predefined keys, default value is “FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown”. Here are the available elements that can be placed inside a paginator.
FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown CurrentPageReport
This is used only in
lino.modlib.comments.RepliesByComment
and should probably be replaced by something else.
- hide_if_empty¶
Don’t show any DataTable at all when there is no data.
This is used only in
lino.modlib.comments.RepliesByComment
and should probably be replaced by something else.
Showing, hiding and formatting sums¶
- class lino.core.actors.Actor
- sum_text_column = 0
The index of the column that should hold the text to display on the totals row (returned by
get_sum_text()
).
- get_sum_text(self, ar, sums)¶
Return the text to display on the totals row. The default implementation returns “Total (N rows)”.
Lino automatically assumes that you want a sum for every numeric field. Sometimes this is now waht you want. In that case you can say:
MyModel.set_widget_option('year", show_sum=False)
When a table has at least one column with a sum, Lino adds a “totals” line when
printing the table. The first empty column in that line will receive a text
“Total (9 rows)”. That text is customizable by overriding
Actor.get_sum_text()
.
If you don’t want that text to appear in the first empty column, you can
specify a value for Actor.sum_text_column
. Usage example: the first
screenshot below is without Actor.sum_text_column
, the second is with
sum_text_column
set to 2: