Welcome | Get started | Dive into Lino | Contribute | Topics | Reference | More

The Model class

Lino extends the Django model

class lino.core.model.Model

Lino extension of Django's database model. This is a subclass of Django's Model class (django.db.models.Model).

In a Lino application you will define your models as direct or indirect subclasses of lino.core.model.Model (usually referred as dd.Model).

When a Lino application imports plain Django Model classes, Lino will "extend" these by adding the attributes and methods defined here to these classes.

Virtual fields

overview

A multi-paragraph representation of this database row.

Customizable using get_overview_elems().

A simple text representation of this database row as a clickable link that opens the detail window.

This is a htmlbox. We don't recommend to override this field directly.

navigation_panel

A virtual field that displays the navigation panel for this row. This may be included in a detail layout, usually either on the left or the right side with full height.

Workflow

workflow_buttons

Shows the current workflow state of this database row and a list of available workflow actions.

workflow_state_field

Optional default value for workflow_state_field of all data views based on this Model.

workflow_owner_field

Optional default value for lino.core.table.Table.workflow_owner_field on all tables based on this Model.

Field-specific customization hooks

You can optionally define some field-specific customization hooks. FOO in this section is the name of a database field defined on the same model (or on a parent).

class lino.core.model.Model
FOO_changed()

Called when field FOO of an instance of this model has been modified through the user interface.

Example:

def city_changed(self, ar):
    print("User {} changed city of {} to {}!".format(
        ar.get_user(), self, self.city))

Note: If you want to know the old value when reacting to a change, consider writing Model.after_ui_save() instead.

FOO_choices()

Return a queryset or list of allowed choices for field FOO.

For every field named "FOO", if the model has a method called "FOO_choices" (which must be decorated by dd.chooser()), then this method will be installed as a chooser for this field.

Example of a context-sensitive chooser method:

country = dd.ForeignKey(
    'countries.Country', blank=True, null=True)
city = dd.ForeignKey(
    'countries.City', blank=True, null=True)

@chooser()
def city_choices(cls,country):
    if country is not None:
        return country.place_set.order_by('name')
    return cls.city.field.remote_field.model.objects.order_by('name')
create_FOO_choice()

For every field named "FOO" for which a chooser exists, if the model also has a method called "create_FOO_choice", then this chooser will be a learning chooser. That is, users can enter text into the combobox, and Lino will create a new database object from it.

This works only if FOO is (1) a foreign key and (2) has a chooser. See also learning foreign key.

get_choices_text(self, request, actor, field)

Return the text to be displayed when an instance of this model is being used as a choice in a combobox of a ForeignKey field pointing to this model. request is the web request, actor is the requesting actor.

The default behaviour is to simply return str(self).

A usage example is lino_xl.lib.countries.Place.

disable_delete(self, ar=None)

Decide whether this database object may be deleted. Return None when there is no veto against deleting this database row, otherwise a translatable message that explains to the user why they can't delete this row.

The argument ar contains the action request that is trying to delete. ar is possibly None when this is being called from a script or batch process.

The default behaviour checks whether there are any related objects which would not get cascade-deleted and thus produce a database integrity error.

You can override this method e.g. for defining additional conditions. Example:

def disable_delete(self, ar=None):
    msg = super(MyModel, self).disable_delete(ar)
    if msg is not None:
        return msg
    if self.is_imported:
        return _("Cannot delete imported records.")

When overriding, be careful to not skip the super method unless you know what you want.

Note that lino.mixins.polymorphic.Polymorphic overrides this.

How your model behaves in regard to other models:

Customize what happens when an instance is created:

Some methods you will use but not override:

  • Model.get_data_elem

  • Model.add_param_filter

  • Model.define_action

  • Model.hide_elements