Defines the Site class. For an overview see Introducing the Site class and Introduction to plugins.

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





configure_plugin(app_label, **kwargs)

Set one or several configuration settings of the given plugin before the SITE has been instantiated.




Simplified copy of django.utils.translation.to_locale, but we need it while the settings module is being loaded, i.e. we cannot yet import django.utils.translation.


LanguageInfo(django_code, name, index, suffix)

A named tuple with four fields:


Site([settings_globals, local_apps])

The base class for a Lino application.

TestSite(*args, **kwargs)

Used to simplify doctest strings because it inserts default values for the two first arguments that are mandatory but not used in our examples.

class, name, index, suffix)

Bases: tuple

A named tuple with four fields:

  • django_code -- how Django calls this language

  • name -- how Lino calls it

  • index -- the position in the Site.languages tuple

  • suffix -- the suffix to append to babel fields for this language

property django_code

Alias for field number 0

property index

Alias for field number 2

property name

Alias for field number 1

property suffix

Alias for field number 3

Simplified copy of django.utils.translation.to_locale, but we need it while the settings module is being loaded, i.e. we cannot yet import django.utils.translation. Also we don't need the to_lower argument., **kwargs)

Set one or several configuration settings of the given plugin before the SITE has been instantiated.

This might get deprecated some day. Consider using the Site.get_plugin_configs() method instead.

See Introduction to plugins.

class, local_apps=[], **kwargs)

Bases: object

The base class for a Lino application. This class is designed to be overridden by both application developers and local site administrators. Your SITE setting is expected to contain an instance of a subclass of this.


An AttrDict with one entry for each installed plugin, mapping the app_label of every plugin to the corresponding lino.core.plugin.Plugin instance.

This attribute is automatically filled by Lino and available as dd.plugins already before Django starts to import modules.


Old name for models. Deprecated.


An AttrDict which maps every installed app_label to the corresponding module object.

This is also available as the shortcut rt.models.

See Introduction to plugins


A tuple in the format expected by Django's choices attribute, used e.g. by LanguageField. It's content is automatically populated from languages and application code should not change it's value.


Until 20180926 this was a string like e.g. 'beid' in order to use a custom protocol for reading eid cards. Now it is deprecated. Use lino_xl.lib.beid.Plugin.urlhandler_prefix instead.

auto_fit_column_widths = True

The default value for the auto_fit_column_widths of tables in this application.

site_locale = None

The locale to use for certain localized things on this site.

Used by format_currency().

This should be a string of type '<language>_<country>.<encoding>', and it must have been generated previously. For example:

sudo locale-gen de_BE.utf8
kernel = None

This attribute is available only after startup(). See lino.core.kernel.

readonly = False

Setting this to True turns this site in a readonly site. This means that DATABASES must point to the DATABASES of some other (non-readonly) site, and that initdb will do nothing.

history_aware_logging = False

Whether to log a message Started %s (using %s) --> PID %s at process startup (and a message Done PID %s at termination).

These two messages are interesting e.g. when a system administrator wants to know which processes have been running on a given production site, but they are usually disturbing during development.

TODO: Replace this setting by an aproach using a second logger lino.archive. Also tidy up usage of lino.utils.dblogger. To be meditated.

See also About logging.

the_demo_date = None

A hard-coded constant date to be used as reference by today() and demo_date(). For example many demo databases have this set because certain tests rely on a constant reference date.

title = None

The title of this web site to appear in the browser window. If this is None, Lino will use verbose_name as default value.

hoster_status_url = ''

This is mentioned in 500.html.

verbose_name = 'yet another Lino application'

The name of this application, to be displayed to end-users at different places.

Note the difference between title and verbose_name:

IOW, the title is rather for usage by local system administrators, while the verbose_name is rather for usage by application developers.

version = None

The version number.

url = None

The URL of the website that describes this application. Used e.g. in a Site ‣ About dialog box.

device_type = 'desktop'

The default device type used on this server. Should be one of 'desktop', 'tablet' or 'mobile'.

This is used by DeviceTypeMiddleware.

obj2text_template = '*{0}*'

The format template to use when rendering a ForeignKey as plain text.

Note: reSTructuredText uses italic and bold. Changing this can cause lots of trivial failures in test suites. It is also used by lino.modlib.notify when generating the mail body.

make_missing_dirs = True

Set this to False if you don't want Lino to automatically create missing directories when needed. If this is False, Lino will raise an exception in these cases, asking you to create it yourself.

project_dir = None

Full path to your local project directory.

This is the directory containing your file (more precisely the directory containing the source file of your DJANGO_SETTINGS_MODULE). Note that when using a settings package, project_dir points to the settings subdir of what getlino calls the project directory.

Lino sets this automatically when the Site initializes.

The project_dir contains some paging subdirectories with special meaning:

  • a config directory will be added to the config search path.

  • a log directory will activate logging to a lino.log file.

  • a migrations directory will activate Django migrations.

  • a media directory contains generated and uploaded files to be published by the webserver

project_name = None

A nickname for this project.

This is used only when LINO_CACHE_ROOT is set, and only to set the cache_dir. In that case all Lino projects in a given repository must have a unique project name.

If this is None, Lino will find a default value by splitting project_dir and taking the last part (or the second-last if the last part is 'settings'.

cache_dir = None

The directory where Lino will create temporary data for this project, including the media directory and the default.db file.

This is either the same as project_dir or (if LINO_CACHE_ROOT is set), will be set to LINO_CACHE_ROOT + project_name.

languages = None

The language distribution used on this site. It has its own chapter The languages of a Site in the Developers Guide.

django_settings = None

This is a reference to the globals() dictionary of your file (the one you provided when instantiating the Site object).

startup_time = None

The time when this Site has been instantiated, in other words the startup time of this Django process. Don't modify this.

top_level_menus = [('master', 'Master'), ('main', None), ('reports', 'Reports'), ('config', 'Configure'), ('explorer', 'Explorer'), ('site', 'Site')]

The list of top-level menu items. See setup_menu().

ignore_model_errors = False

Not yet sure whether this is needed. Maybe when generating documentation.

loading_from_dump = False

Whether the process is currently loading data from a Python dump.

When loading from a python dump, application code should not generate certain automatic data because that data is also part of the dump.

This is normally False, but a Python dump created with dump2py explicitly calls install_migrations() which sets this to True.

Application code should not change this setting except for certain special test cases.

migration_class = None

If you maintain a data migrator module for your application, specify its name here.

See Database Migration and/or lino.utils.dpy.install_migrations().

TODO: rename this to migrator_class

hidden_languages = None

A string with a space-separated list of django codes of languages that should be hidden.

Lino Welfare uses this because the demo database has 4 languages of which one (nl) is hidden by default.

partners_app_label = 'contacts'

Temporary setting, see Polymorphism.

trusted_templates = False

Set this to True if you are sure that the users of your site won't try to misuse Jinja's capabilities.

allow_duplicate_cities = False

In a default configuration (when allow_duplicate_cities is False), Lino declares a UNIQUE clause for Places to make sure that your database never contains duplicate cities. This behaviour might disturb e.g. when importing legacy data that did not have this restriction. Set it to True to remove the UNIQUE clause.

Changing this setting might affect your database structure and thus require a Database Migration if your application uses lino_xl.lib.countries.

uid = 'myuid'

A universal identifier for this Site. This is needed when synchronizing with CalDAV server. Locally created calendar components in remote calendars will get a UID based on this parameter, using "%s@%s" % (, settings.SITE.kernel).

The default value is 'myuid', and you should certainly override this on a production server that uses remote calendars.

project_model = None

Optionally set this to the full name of a model used as "central project" in your application. Models which inherit from ProjectRelated then have an additional ForeignKey to this model.

TODO: convert this into a plugin setting (probably of the office plugin).

user_model = None

The database model used for users. This is automatically set to 'users.User' when lino.modlib.users is installed.

Default value is None, meaning that this application has no user management. See also set_user_model()

See Authentication.

social_auth_backends = None

A list of backends for Python Social Auth (PSA).

Having this at a value different from None means that this site uses authentication via third-party providers.

Sites which use this feature must also install PSA into their environment (which is done automatically by install).

Depending on the backend you must also add credentials in your local file, e.g.:


A working example is in the demo project.

use_linod = False

Whether this site uses the Lino daemon for running scheduled tasks. See The Lino Daemon.

use_security_features = False

Set this to True in order to activate a selection of security features to protect against miscellaneous attacks. You can do this only if your application is being served via HTTPS. The idea is to provide a reasonable security out of the box.

This will activate some middleware and set some security-related settings. This is a new feature and not much tested. As a hoster you may prefer adding security manually using your established standards (regarding security Lino does not add anything to plain Django). See also Security of Lino applications.

use_ipdict = False

Whether this site uses lino.modlib.ipdict.

Note that lino.modlib.ipdict unlike normal plugins should not be installed by adding it to your get_installed_apps() method but by setting this attribute. This approach has the advantage of also setting MIDDLEWARE_CLASSES automatically.

auth_middleware = None

Override used authorisation middlewares with supplied tuple of middleware class names.

If None, use logic described in Authentication

user_types_module = None

The name of the user types module to be used on this site.

Default value is None, meaning that permission control is inactive: everything is permitted. But note that set_user_model() sets it to lino.core.user_types.

This must be set if you want to enable permission control based on user roles defined in Permittable.required_roles and UserType.role.

If set, Lino will import the named module during site startup. It is expected to define application-specific user roles (if necessary) and to fill the UserTypes choicelist.

For example:

class Site(Site):
    user_types_module = 'myapp.user_types'

Examples of such user types modules are lino.core.user_types and lino_noi.lib.noi.user_types.

workflows_module = None

The full Python path of the workflows module to be used on this site.

custom_layouts_module = None

The full Python path of the custom layouts module used on this site.

legacy_data_path = None

Used by custom fixtures that import data from some legacy database.

propvalue_max_length = 200

Used by

show_internal_field_names = True

Whether the internal field names should be visible. ExtUI implements this by prepending them to the tooltip, which means that use_quicktips must also be True. Default is True.

never_build_site_cache = False

Set this to True if you want that Lino never (re)builds the site cache, even when asked. This can be useful on a development server when you are debugging directly on the generated lino*.js. Or for certain unit test cases.

build_js_cache_on_startup = False

Whether the Javascript cache files should be built on startup for all user profiles and languages.

On a production server this should be True for best performance, but often this is not necessary, so default value is False, which means that each file is built upon need (when a first request comes in).

You can also set it to None, which means that Lino decides automatically during startup: it becomes False if either lino.core.utils.is_devserver() returns True or setting:DEBUG is set.


If a variable of that name is set, then Lino will override the code value and set build_js_cache_on_startup to True.

keep_erroneous_cache_files = False

When some exception occurs during lino.core.kernel.Kernel.make_cache_file(), Lino usually removes the partly generated file to make sure that it will try to generate it again (and report the same error message) for every subsequent next request.

Set this to True if you need to see the partly generated cache file. Don't forget to remove this when you have inspected the file and fixed the reason of the exception, because if this is True and some next exception occurs (which will happen sooner or later), then all subsequent requests will usually end up to the user with a blank screen and (if they notice it), a message TypeError: Lino.main_menu is undefined in their Javascript console.

use_websockets = False

Set this to True in order to activate use of websockets and channels.

This setting is currently used only by lino.modlib.notify, so its setting is ignored if your application doesn't use that plugin.

If you use lino.modlib.notify and change this setting to True, then you need to install django-channels:

pip install channels
use_java = True

A site-wide option to disable everything that needs Java. Note that it is up to the plugins which include Java applications to respect this setting. Usage example is lino_xl.lib.beid.

use_silk_icons = False

If this is True, certain Lino plugins use the deprecated silk icons library for representing workflows.

The recommended but not yet fully implemented "modern" style is to use unicode symbols instead of icons.

use_new_unicode_symbols = False

Whether to use "new" unicode symbols (e.g. from the Miscellaneous Symbols and Pictographs block) which are not yet implemented in all fonts.

Currently used by lino_noi.lib.noi.workflows

use_experimental_features = False

Whether to include "experimental features". Deprecated. lino_xl.lib.inspect

site_config_defaults = {}

Default values to be used when creating the site_config.

Usage example:

site_config_defaults = dict(default_build_method='appypdf')
default_build_method = None

The default build method to use when rendering printable documents.

This is the last default value, used only when default_build_method in SiteConfig is empty.

is_demo_site = True

When this is True, then this site runs in "demo" mode. "Demo mode" means:

  • the welcome text for anonymous users says "This demo site has X users, they all have "1234" as password", followed by a list of available usernames.

Default value is True. On a production site you will of course set this to False.

See also demo_fixtures and the_demo_date.

demo_fixtures = []

The list of fixtures to be loaded by the prep command. See also Demo fixtures.

django_admin_prefix = None

The prefix to use for Django admin URLs. Leave this unchanged as long as docs/tickets/70 is not solved.

calendar_start_hour = 7

The first hour of a work day.

Limits the choices of a lino.core.fields.CalendarTimeField.

calendar_end_hour = 21

The last hour of a work day.

Limits the choices of a lino.core.fields.CalendarTimeField.

time_format_extjs = 'H:i'

Format (in ExtJS syntax) to use for displaying dates to the user. If you change this setting, you also need to override parse_time().

alt_time_formats_extjs = 'g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A|Hi|g.ia|g.iA|g.i a|g.i A|h.i|g.i|H.i'

Alternative time entry formats accepted by ExtJS time widgets.

ExtJS default is:

"g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A"

Lino's extended default also includes:

"Hi" (1900) and "g.ia|g.iA|g.i a|g.i A|h.i|g.i|H.i" (Using . in replacement of ":")

date_format_extjs = 'd.m.Y'

Format (in ExtJS syntax) to use for displaying dates to the user. If you change this setting, you also need to override parse_date().

alt_date_formats_extjs = 'd/m/Y|Y-m-d'

Alternative date entry formats accepted by ExtJS Date widgets.

uppercase_last_name = False

Whether last name of persons should (by default) be printed with uppercase letters. See lino.test_apps.human

jasmine_root = None

Path to the Jasmine root directory. Only used on a development server if the media directory has no symbolic link to the Jasmine root directory and only if use_jasmine is True.

default_user = None

Username of the user to be used for all incoming requests. Setting this to a nonempty value will disable authentication on this site. The special value 'anonymous' will cause anonymous requests (whose user attribute is the AnonymousUser singleton).

See also get_auth_method().

This setting should be None when user_model is None.

remote_user_header = None

The name of the header (set by the web server) that Lino should consult for finding the user of a request. The default value None means that http authentication is not used. Apache's default value is "REMOTE_USER".

use_eid_applet = False

Whether to include functionality to read Belgian id cards using the official eid-applet. This option is experimental and doesn't yet work. See /blog/2012/1105.

use_esteid = False

Whether to include functionality to read Estonian id cards. This option is experimental and doesn't yet work.

use_filterRow = False

See /blog/2011/0630. This option was experimental and doesn't yet work (and maybe never will).

use_awesome_uploader = False

Whether to use AwesomeUploader. This option was experimental and doesn't yet work (and maybe never will).

use_tinymce = True

Replaced by lino.modlib.tinymce.

use_jasmine = False

Whether to use the Jasmine testing library.

use_quicktips = True

Whether to make use of Ext.QuickTips for displaying Help Texts and internal field names (if show_internal_field_names).

use_css_tooltips = False

Whether to make use of CSS tooltips when displaying help texts defined in lino.models.HelpText.

use_vinylfox = False

Whether to use VinylFox extensions for HtmlEditor. This feature was experimental and doesn't yet work (and maybe never will). See /blog/2011/0523.

webdav_root = None

The path on server to store webdav files. Default is cache_dir + ´/media/webdav'.

webdav_url = None

The URL location for webdav files. In a normal production configuration you should leave this unchanged and Lino will set the default value '/media/webdav/'. You also need to configure your web server to actually serve the files below this location using the WebDAV protocol. See WebDAV.

Obsolete: This may be used to simulate a WebDAV location on a development server. For example on a Windows machine, you may set it to w:\, and before invoking runserver, you issue in a command prompt:

subst w: <dev_project_path>\media\webdav
webdav_protocol = None

Set this to a string like e.g. 'wdav' to instruct Lino to use this custom protocol instead of http when linking to "editable" printable documents.

See WebDAV.

sidebar_width = 0

Used by lino.modlib.plain. Width of the sidebar in 1/12 of total screen width. Meaningful values are 0 (no sidebar), 2 or 3.

config_id = 1

The primary key of the one and only SiteConfig instance of this Site. Default value is 1.

This is Lino's equivalent of Django's SITE_ID setting. Lino applications don't need django.contrib.sites (The "sites" framework) because an analog functionality is provided by lino.modlib.system.

preview_limit = 15

Default value for the preview_limit parameter of all tables who don't specify their own one. Default value is 15.

default_ui = 'lino.modlib.extjs'

The full Python name of the plugin which is to be used as default user interface on this Site.

Default value is lino.modlib.extjs. Other candidates are lino_react.react, lino.modlib.bootstrap3, lino_xl.lib.pages and lino_extjs6.extjs6 .

Another possibility is to set it to None. In that case you will probably also set root_urlconf to a custom URL dispatcher. Usage example for this see lino.projects.cms.

design_name = 'desktop'

Deprecated. We plan to remove the design_name attribute.

The name of the design to use. The default value is 'desktop'. The value should be one of 'desktop' or 'mobile'.

For every plugin, Lino will try to import its "design module". For example if design_name is 'desktop', then the design module for a plugin '' is ''. If such a module exists, Lino imports it and adds it to The result is the same as if there were a from .desktop import * statement at the end of the module.

root_urlconf = 'lino.core.urls'

The value to be attribute to ROOT_URLCONF when this Site instantiates.

The default value is lino.core.urls.

bleach_allowed_tags = ['a', 'b', 'i', 'em', 'ul', 'ol', 'li', 'strong', 'p', 'br', 'span', 'pre', 'def', 'div', 'table', 'th', 'tr', 'td', 'thead', 'tfoot', 'tbody']

A list of tag names which are to remain in HTML comments if bleaching is active.

See Bleaching.

textfield_bleached = True

Default value for RichTextField.textfield_bleached.

See Bleaching.

textfield_format = 'plain'

The default format for text fields. Valid choices are currently 'plain' and 'html'.

Text fields are either Django's models.TextField or lino.core.fields.RichTextField.

You'll probably better leave the global option as 'plain', and specify explicitly the fields you want as html by declaring them:

foo = fields.RichTextField(...,format='html')

We even recommend that you declare your plain text fields also using fields.RichTextField and not models.TextField:

foo = fields.RichTextField()

Because that gives subclasses of your application the possibility to make that specific field html-formatted:

log_each_action_request = False

Whether Lino should log every incoming request for non readonly actions.

This is experimental. Theoretically it is useless to ask Lino for logging every request since Apache does this. OTOH Lino can produce more readable logs.

Note also that there is no warranty that actually each request is being logged. It corrently works only for requests that are being processed by the kernel's run_action methods.

verbose_client_info_message = False

Set this to True if actions should send debug messages to the client. These will be shown in the client's Javascript console only.

help_email = ''

An e-mail address where users can get help. This is included in admin_main.html.

catch_layout_exceptions = True

Lino usually catches any exception during startup (in create_layout_element) to report errors of style "Unknown element "postings.PostingsByController ('postings')" referred in layout <PageDetail on pages.Pages>."

Setting this to False is useful when there's some problem within the framework.

strict_dependencies = True

This should be True unless this site is being used just for autodoc or similar applications.

strict_choicelist_values = True

Whether invalid values in a ChoiceList should raise an exception.

This should be True except for exceptional situations. Setting this to True won't allow you to store invalid choicelist values in the database, but at least Lino will not raise an exception as soon as it reads an invalid value from existing data. This can happen e.g. after a code upgrade without data migration. In such a situation you may want to run in order to migrate the data.

csv_params = {}

Site-wide default parameters for CSV generation. This must be a dictionary that will be used as keyword parameters to Python csv.writer()

Possible keys include:

logger_filename = 'lino.log'

The name of Lino's main log file, created in setup_logging().

See also About logging.

auto_configure_logger_names = 'schedule atelier django lino radicale'

A string with a space-separated list of logger names to be automatically configured. See setup_logging().

appy_params = {'ooPort': 8100, 'pythonWithUnoPath': '/usr/bin/python3', 'raiseOnError': True}

Used by lino_xl.lib.appypod.choicelist.AppyBuildMethod.

Allowed keyword arguments for appy.pod.renderer.Render are:


See the source code for details.

See also Running a LibreOffice server

decimal_separator = ','

Set this to either '.' or ',' to define wether to use comma or dot as decimal point separator when entering a DecimalField.

decimal_group_separator = '\xa0'

Decimal group separator for decfmt().

time_format_strftime = '%H:%M'

Format (in strftime syntax) to use for displaying dates to the user. If you change this setting, you also need to override parse_time().

date_format_strftime = '%d.%m.%Y'

Format (in strftime syntax) to use for displaying dates to the user. If you change this setting, you also need to override parse_date().

date_format_regex = '/^[0123]?\\d\\.[01]?\\d\\.-?\\d+$/'

Format (in Javascript regex syntax) to use for displaying dates to the user. If you change this setting, you also need to override parse_date().

datetime_format_strftime = '%Y-%m-%dT%H:%M:%S'

Format (in strftime syntax) to use for formatting timestamps in AJAX responses. If you change this setting, you also need to override parse_datetime().

datetime_format_extjs = 'Y-m-d\\TH:i:s'

Format (in ExtJS syntax) to use for formatting timestamps in AJAX calls. If you change this setting, you also need to override parse_datetime().

override_modlib_models = None

A dictionary which maps model class names to the plugin which overrides them.

This is automatically filled at startup. You can inspect it, but you should not modify it. Needed for is_abstract_model().

The challenge is that we want to know exactly where every model's concrete class will be defined before actually starting to import the modules. That's why we need extends_models.

This can be tricky, see e.g. 20160205.

installed_plugin_modules = None

Used internally by is_abstract_model(). Don't modify.

A set of the full Python paths of all imported plugin modules. Not just the plugin modules themselves but also those they inherit from.

migrations_package = None

The full Python name of the local package that holds Django migrations for all plugins of this site.

You might manually specify a name, but the recommended way is to create a migrations directory. See Django migrations on a Lino site.

confdirs = None

Pointer to the config directories registry. See Introduction to config directories and lino.utils.config. Lino sets this attribute during site startup.

init_before_local(settings_globals, local_apps)

If your project_dir contains no, but does contain a fixtures subdir, then Lino automatically adds this as a local fixtures directory to Django's FIXTURE_DIRS.

But only once: if your application defines its own local fixtures directory, then this directory "overrides" those of parent applications. E.g. does not want to load the application-specific fixtures of


Modifies the DEFAULT_LOGGING setting.

This is called before any plugins are loaded because all this must happen before Django passes the setting to the logging.config.dictConfig function.

It is designed to work with the LOGGING and LOGGER_CONFIG settings unmodified.

It does the following modifications:

  • Define a default logger configuration which is initially the same as the one used by Django:

        'handlers': ['console', 'mail_admins'],
        'level': 'INFO',
  • If the project_dir has a subdirectory named log, and if logger_filename is not empty, add a handler named file and a formatter named verbose, and add that handler to the default logger configuration.

  • Apply the default logger configuration to every logger name in auto_configure_logger_names.

  • (does no longer) configure the console handler to write to stdout instead of Django's default stderr (as explained here) because that breaks testing.

It does nothing at all if auto_configure_logger_names is set to None or empty.

See also About logging.


Return a dict to be set as the DATABASE setting.

The default behaviour uses SQLite (1) on a file named default.db in the cache_dir if that attribute is specified, and (2) in :memory: when cache_dir is None.

And alternative might be for example:

def get_database_settings(self):
    return {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'test_' + self.project_name,
            'USER': 'django',
            'PASSWORD': os.environ['MYSQL_PASSWORD'],
            'HOST': 'localhost',
            'PORT': 3306,
            'OPTIONS': {
               "init_command": "SET storage_engine=MyISAM",

See Site-wide default settings.


Return a series of plugin configuration settings.

This is called before plugins are loaded. rt.plugins is not yet populated.

The method must return an iterator that yields tuples with three items each: The name of the plugin, the name of the setting and the value to set.


Load all plugins and build the INSTALLED_APPS setting for Django.

This includes a call to get_apps_modifiers() and get_installed_apps().


Collect requirements from plugins. Add some more requirements which depend on options in the local file.


Collect modules


Collect modules.

Deprecated. We plan to remove the design_name attribute.

Note the situation when a module exists but causes itself an ImportError because it contains a programming mistake. In that case we want the traceback to occur, not to silently do as if no module existed.

install_help_text(fld, cls=None, attrname=None)

Set the help_text attribute of the given element fld from collected


Deprecated. Use get_plugin_configs() instead.

This method is called exactly once during site startup, after load_plugins() but before populating the models registry.

See Introduction to plugins.


When LINO_CACHE_ROOT is set, Lino adds a stamp file called lino_cache.txt to every project's cache directory in order to avoid duplicate use of same cache directory.


A small text file with one line of text which contains the path of the project which uses this cache directory.


This can be called during the on_init of plugins which provide user management (the only plugin which does this is currently lino.modlib.users).


Returns the authentication method used on this site. This is one of None, 'remote' or 'session'.

It depends on the values in user_model, default_user and remote_user_header.

It influences the results of get_middleware_classes() and get_installed_apps(), and the content of AUTHENTICATION_BACKENDS.


Override or hide individual plugins of an existing application.

Deprecated because this approach increases complexity instead of simplifying things.

For example, if your site inherits from lino.projects.min2:

def get_apps_modifiers(self, **kw):
    return kw

The default implementation returns an empty dict.

This method adds an additional level of customization because it lets you remove or replace individual plugins from INSTALLED_APPS without rewriting your own get_installed_apps().

This will be called during Site instantiation and is expected to return a dict of app_label to full_python_path mappings which you want to override in the list of plugins returned by get_installed_apps().

Mapping an app_label to None will remove that plugin from INSTALLED_APPS.

It is theoretically possible but not recommended to replace an existing app_label by an app with a different app_label. For example, the following might work but is not recommended:


Return True if the app is known, but has been disabled using get_apps_modifiers().


This may be called from within a


Same as update_settings(), but raises an exception if a setting already exists.

TODO: Currently this exception is deactivated. Because it doesn't work as expected. For some reason (maybe because settings is being imported twice on a devserver) it raises a false exception when override_defaults() tries to use it on MIDDLEWARE_CLASSES...


Start up this Site.

You probably don't want to override this method as it might be called several times. e.g. under mod_wsgi: another thread has started and not yet finished startup().

If you want to run custom code on site startup, override do_site_startup().


This method is called exactly once during site startup, just between the pre_startup and the post_startup signals. A hook for subclasses.

TODO: rename this to on_startup?

If you override it, don't forget to call the super method.

property logger

This must not be used before Django has done it logging config. For example don't use it in a module.


Yield all (existing) directories named subdir_name of this Site's project directory and its inherited project directories.


Make missing directories if they don't exist and if make_missing_dirs is True.

is_abstract_model(module_name, model_name)

Return True if the named model is declared as being extended by lino.core.plugin.Plugin.extends_models.

Typical usage:

class MyModel(dd.Model):
     class Meta:
         abstract = dd.is_abstract_model(__name__, 'MyModel')

See Plugin inheritance.


Deprecated. This feature was a bit too automagic and caused bugs to pass silently. See e.g. 2013-10-25.


Return True if INSTALLED_APPS contains an item which ends with the specified app_label.

setup_model_spec(obj, name)

If the value of the named attribute of obj is a string, replace it by the model specified by that string.

Example usage:

# library code:
class ThingBase(object):
    the_model = None

    def __init__(self):
        settings.SITE.setup_model_spec(self, 'the_model')

# user code:
class MyThing(ThingBase):
    the_model = "contacts.Partner"
on_each_app(methname, *args)

Call the named method on the module of each installed app.

Note that this mechanism is deprecated. It is still used (on names like setup_workflows and setup_site) for historical reasons but will disappear one day.

for_each_app(func, *args, **kw)

Call the given function on each installed plugin. Successor of on_each_app().

This also loops over plugins that don't have a models module and the base plugins of plugins which extend some plugin.

demo_date(*args, **kwargs)

Deprecated. Should be replaced by today(). Compute a date using lino.utils.date_offset() based on the process startup time (or the_demo_date if this is set).

Used in Python fixtures and unit tests.

today(*args, **kwargs)

Almost the same as

One difference is that the system's today is replaced by the_demo_date if that attribute is set.

Another difference is that arguments can be passed to add some offset. See atelier.utils.date_offset().

This feature is being used in many test cases where e.g. the age of people would otherwise change.


Returns the text to display in a console window when this application starts.


Text to display in a console window when Lino starts.


Used in footnote or header of certain printed documents.


See lino.utils.dpy.install_migrations().


Convert a string formatted using date_format_strftime or date_format_extjs into a (y,m,d) tuple (not a instance). See /blog/2010/1130.


Convert a string into a datetime.time instance using regex. only supports hours and min, not seconds.


Convert a string formatted using datetime_format_strftime or datetime_format_extjs into a datetime.datetime instance.


Call lino_resolve_type after startup.


Hook for subclasses to add or modify actions.


Yield a list of (name, version, url) tuples describing the third-party software used on this site.

This function is used by using_text() and welcome_html().


This function is called when a Site object gets instantiated, i.e. while Django is still loading the settings. It analyzes the languages attribute and converts it to a tuple of LanguageInfo objects.


Reduce Django's LANGUAGES to my languages. Note that are not yet translated, we take these from django.conf.global_settings.


Use this in Python fixtures or tests to test whether a Site instance supports a given language. code must be a Django-style language code.

On a site with only one locale of a language (and optionally some other languages), you can use only the language code to get a tuple of LanguageInfo objects.

>>> from import TestSite as Site
>>> Site(languages="en-us fr de-be de").get_language_info('en')
LanguageInfo(django_code='en-us', name='en_US', index=0, suffix='')

On a site with two locales of a same language (e.g. 'en-us' and 'en-gb'), the simple code 'en' yields that first variant:

>>> site = Site(languages="en-us en-gb")
>>> print(site.get_language_info('en'))
LanguageInfo(django_code='en-us', name='en_US', index=0, suffix='')

This is used by UserType.


>>> from import TestSite as Site
>>> lst = Site(languages="en fr de nl et pt").resolve_languages('en fr')
>>> [ for i in lst]
['en', 'fr']

You may not specify languages which don't exist on this site:

>>> Site(languages="en fr de").resolve_languages('en nl')
Traceback (most recent call last):
Exception: Unknown language code 'nl' (must be one of ['en', 'fr', 'de'])

The django code of the default language to use in every lino.utils.mldbc.fields.LanguageField.

str2kw(name, txt, **kw)

Return a dictionary which maps the internal field names for babelfield name to their respective translation of the given lazy translatable string txt.

>>> from django.utils.translation import ugettext_lazy as _
>>> from import TestSite as Site
>>> site = Site(languages='de fr es')
>>> site.str2kw('name', _("January")) == {'name_fr': 'janvier', 'name': 'Januar', 'name_es': 'Enero'}
>>> site = Site(languages='fr de es')
>>> site.str2kw('name', _("January")) == {'name_de': 'Januar', 'name': 'janvier', 'name_es': 'Enero'}
babelkw(name, **kw)

Return a dict with appropriate resolved field names for a BabelField name and a set of hard-coded values.

You have some hard-coded multilingual content in a fixture: >>> from import TestSite as Site >>> kw = dict(de="Hallo", en="Hello", fr="Salut")

The field names where this info gets stored depends on the Site's languages distribution.

>>> Site(languages="de-be en").babelkw('name',**kw) == {'name_en': 'Hello', 'name': 'Hallo'}
>>> Site(languages="en de-be").babelkw('name',**kw) == {'name_de_BE': 'Hallo', 'name': 'Hello'}
>>> Site(languages="en-gb de").babelkw('name',**kw) == {'name_de': 'Hallo', 'name': 'Hello'}
>>> Site(languages="en").babelkw('name',**kw) == {'name': 'Hello'}
>>> Site(languages="de-be en").babelkw('name',de="Hallo",en="Hello") == {'name_en': 'Hello', 'name': 'Hallo'}

In the following example babelkw attributes the keyword de to the first language variant:

>>> Site(languages="de-ch de-be").babelkw('name',**kw) == {'name': 'Hallo'}
args2kw(name, *args)

Takes the basename of a BabelField and the values for each language. Returns a dict mapping the actual fieldnames to their values.

field2kw(obj, name, **known_values)

Return a dict with all values of the BabelField name in the given object obj. The dict will have one key for each languages.


>>> from import TestSite as Site
>>> from lino.utils import AttrDict
>>> def testit(site_languages):
...     site = Site(languages=site_languages)
...     obj = AttrDict(site.babelkw(
...         'name', de="Hallo", en="Hello", fr="Salut"))
...     return site,obj
>>> site, obj = testit('de en')
>>> site.field2kw(obj, 'name') == {'de': 'Hallo', 'en': 'Hello'}
>>> site, obj = testit('fr et')
>>> site.field2kw(obj, 'name') == {'fr': 'Salut'}
field2args(obj, name)

Return a list of the babel values of this field in the order of this Site's Site.languages attribute.

babelitem(*args, **values)

Given a dictionary with babel values, return the value corresponding to the current language.

This is available in templates as a function tr.

>>> kw = dict(de="Hallo", en="Hello", fr="Salut")
>>> from import TestSite as Site
>>> from django.utils import translation

A Site with default language "de":

>>> site = Site(languages="de en")
>>> tr = site.babelitem
>>> with translation.override('de'):
...    print(tr(**kw))
>>> with translation.override('en'):
...    print(tr(**kw))

If the current language is not found in the specified values, then it returns the site's default language:

>>> with translation.override('jp'):
...    print(tr(en="Hello", de="Hallo", fr="Salut"))

Testing detail: default language should be "de" in our example, but we are playing here with more than one Site instance while Django knows only one "default language" which is the one specified in

Another way is to specify an explicit default value using a positional argument. In that case the language's default language doesn'n matter:

>>> with translation.override('jp'):
...    print(tr("Tere", de="Hallo", fr="Salut"))
>>> with translation.override('de'):
...     print(tr("Tere", de="Hallo", fr="Salut"))

You may not specify more than one default value:

>>> tr("Hello", "Hallo")
Traceback (most recent call last):
ValueError: ('Hello', 'Hallo') is more than 1 default value.
babelattr(obj, attrname, default=<class ''>, language=None)

Return the value of the specified babel field attrname of obj in the current language.

This is to be used in multilingual document templates. For example in a document template of a Contract you may use the following expression:

babelattr(self.type, 'name')

This will return the correct value for the current language.


>>> from __future__ import unicode_literals
>>> from django.utils import translation
>>> from import TestSite as Site
>>> from lino.utils import AttrDict
>>> def testit(site_languages):
...     site = Site(languages=site_languages)
...     obj = AttrDict(site.babelkw(
...         'name', de="Hallo", en="Hello", fr="Salut"))
...     return site, obj
>>> site,obj = testit('de en')
>>> with translation.override('de'):
...     print(site.babelattr(obj,'name'))
>>> with translation.override('en'):
...     print(site.babelattr(obj,'name'))

If the object has no translation for a given language, return the site's default language. Two possible cases:

The language exists on the site, but the object has no translation for it:

>>> site,obj = testit('en es')
>>> with translation.override('es'):
...     print(site.babelattr(obj, 'name'))

Or a language has been activated which doesn't exist on the site:

>>> with translation.override('fr'):
...     print(site.babelattr(obj, 'name'))

Returns a string with a diagnostic report about this site. diag is a command-line shortcut to this.


Return whether the specified Partner instance obj is to be considered as imported from some legacy database.


Used in footnote or header of certain printed documents.

The convention is to call it as follows from an appy.pod template (use the html function, not xhtml)

do text
from html(settings.SITE.site_header())

Note that this is expected to return a unicode string possibly containing valid HTML (not XHTML) tags for formatting.


Expected to yield a sequence of items to be rendered on the dashboard (admin_main.html).

The default implementation calls get_dashboard_items on every installed plugin and yields all items.

The items will be rendered in that order, except if lino.modlib.dashboard is installed to enable per-user customized dashboard.

property site_config

This property holds a cached version of the one and only SiteConfig row that holds site-wide database-stored and web-editable Site configuration parameters.

If no instance exists (which happens in a virgin database), we create it using default values from site_config_defaults.

This is always None when lino.modlib.system is not installed.


Clear the cached SiteConfig instance.

This is needed e.g. when the test runner has created a new test database.

Override this to define a series of quick links to appear below the main menu bar.


Return this site's main menu for the given UserType. Must be a lino.core.menus.Toolbar instance. Applications usually should not need to override this.

setup_menu(user_type, main)

Set up the application's menu structure.

See The application menu and The XL and application menus.


Yields the strings to be stored in the MIDDLEWARE_CLASSES setting.

In case you don't want to use this method for defining MIDDLEWARE_CLASSES, you can simply set MIDDLEWARE_CLASSES in your after the Site has been instantiated.

Django and standard HTTP authentication

get_main_html(request, **context)

Return a chunk of html to be displayed in the main area of the admin index. This is being called only if get_main_action() returns None. The default implementation renders the admin_main.html template.


Yields a list of "welcome messages" (see lino.core.actors.Actor.get_welcome_messages()) of all actors. This is being called from admin_main.html.

add_welcome_handler(func, actor=None, msg=None)

Add the given callable as a "welcome handler". Lino will call every welcome handler for every incoming request, passing them a BaseRequest instance representing this request as positional argument. The callable is expected to yield a series of messages (usually either 0 or 1). Each message must be either a string or a E.span element.


Yield the list of apps to be installed on this site. Each item must be either a string (unicode being converted to str) or a generator which will be iterated recursively (again expecting either strings or generators of strings).

Lino will call this method exactly once when the Site instantiates. The resulting list of names will then possibly altered by the get_apps_modifiers() method before being assigned to the INSTALLED_APPS setting.

server_url = ''

The "official" URL used by "normal" users when accessing this Lino site.

This is used by templates such as summary.eml (used by lino.modlib.notify to send notification emails)

Django has a HttpRequest.build_absolute_uri() method, but e.g. notification emails are sent via linod where no HttpRequest exists. That's why we need to manually set server_url.

site_prefix = '/'

The string to prefix to every URL of the Lino web interface.

This must start and end with a *slash. Default value is '/'.

This must be set if your project is not being served at the "root" URL of your server.

If this is different from the default value, Lino also sets SESSION_COOKIE_PATH.

When this Site is running under something else than a development server, this setting must correspond to your web server's configuration. For example if you have:

WSGIScriptAlias /foo /home/luc/mypy/lino_sites/foo/

Then your should specify:

site_prefix = '/foo/'

See also Mass hosting Lino applications.

send_email(subject, sender, body, recipients)

Send an email message with the specified arguments (the same signature as django.core.mail.EmailMessage.

recipients is an iterator over a list of strings with email addresses. Any address containing '' will be removed. Does nothing if the resulting list of recipients is empty.

If body starts with "<", then it is considered to be HTML.


Return a HTML version of the "This is APPLICATION version VERSION using ..." text. to be displayed in the About dialog, in the plain html footer, and maybe at other places.

login(username=None, **kw)

Open a session as the user with the given username.

For usage from a shell or a tested document. Does not require any password because when somebody has command-line access we trust that she has already authenticated.

It returns a BaseRequest object.


Returns a string like "Eupen, den 26. August 2013".

decfmt(v, places=2, **kw)

Format a Decimal value using lino.utils.moneyfmt(), but applying the site settings lino.Lino.decimal_group_separator and lino.Lino.decimal_separator.

>>> from import TestSite as Site
>>> from decimal import Decimal
>>> self = Site()
>>> print(self.decimal_group_separator)
>>> print(self.decimal_separator)
>>> x = Decimal(1234)
>>> print(self.decfmt(x))
1 234,00
>>> print(self.decfmt(x, sep="."))
>>> self.decimal_group_separator = '.'
>>> print(self.decfmt(x))
>>> self.decimal_group_separator = "oops"
>>> print(self.decfmt(x))
format_currency(*args, **kwargs)

Return the given number as a string formatted according to the site_locale setting on this site.

All arguments are forwarded to locale.locale().

lookup_filter(fieldname, value, **kw)

Return a models.Q to be used if you want to search for a given string in any of the languages for the given babel field.

class*args, **kwargs)


Used to simplify doctest strings because it inserts default values for the two first arguments that are mandatory but not used in our examples.


>> from import Site
>> Site(globals(), ...)

>> from import TestSite as Site >> Site(...)