Introduction to plugins¶
A plugin is a Python package that can be "installed" by a Lino application. It encapsulates a set of functionality designed to be potentially used in more than on application.
A Lino plugin corresponds to what Django calls an "application". Lino's
Plugin class is comparable to Django's
but has some additional features, which makes that they are the preferred way.
The plugin developer defines a plugin in the
__init__.py file of the
package. Lino expects this file to define a class named
inherits from the abstract base
Plugin class holds the metadata
about your plugin: configuration values, menu commands, dependencies, ...
Here is a fictive example:
from lino.api import ad, _ class Plugin(ad.Plugin): verbose_name = _("Better calendar") extends = 'lino_xl.lib.cal' needs_plugins = ['lino_xl.lib.contacts'] def setup_main_menu(self, site, user_type, m): m = m.add_menu(self.app_label, self.verbose_name) m.add_action('cal.Teams') m.add_action('cal.Agendas')
A plugin can depend on other plugins by specifying them in the
attribute. This means that when you install this plugin, Lino will
automatically install these other plugins as well
A plugin can define a set of menu commands using methods like
setup_main_menu. This is explained in
The application menu.
Django developers are used to code like this:
from myapp.models import Foo def print_foo(pk=1): print(Foo.objects.get(pk=pk))
In Lino we prefer to use the
rt.models dict as
from lino.api import rt def print_foo(pk=1): Foo = rt.models.myapp.Foo print(Foo.objects.get(pk=pk))
This approach has the advantage of providing Plugin inheritance. One of the basic reasons for using plugins is that users of some plugin can extend it and use their extension instead of the original plugin. Which means that the plugin developer does not know (and does not want to know) where the model classes are actually defined.
rt.models is populated only
after having imported the models. So you cannot use it at the
module-level namespace of a
models.py module. For example
the following variant of above code would not work:
from lino.api import rt Foo = rt.models.foos.Foo # error `AttrDict has no item "foos"` def print_foo(pk=1): print(Foo.objects.get(pk=pk))
Plugins can have attributes for holding configurable options.
Examples of configurable plugin attributes:
The values of plugin attributes can be configured at three levels.
As a plugin developer you specify a hard-coded default value.
class Site(Site): def get_plugin_configs(self): yield super(Site, self).get_plugin_configs() yield ('countries', 'country_code', 'BE') yield ('contacts', 'hide_region', True)
The old style works also:
class Site(Site): def setup_plugins(self): super(Site, self).setup_plugins() self.plugins.countries.configure(country_code='BE') self.plugins.contacts.configure(hide_region=True)
As a system administrator you can override these configuration
defaults in your project's
settings.py using one of the
by overriding the Site class as described above for application developers
by setting the value directly after instantiation of your
Another (deprecated) method is by using the
from lino_cosi.lib.cosi.settings import * configure_plugin('countries', country_code='BE') SITE = Site(globals())
Beware the pitfall:
configure_plugin() must be called before the
SITE has been instantiated, otherwise they will be ignored
silently. (It is not easy to prevent accidental calls to it after
Site initialization because there are scenarios where you want to
instantiate several Site objects.)
Keep in mind that you can indeed never be sure that your
instance is actually being used. A local system admin can always decide to
settings.py module and to re-instantiate your Site class
another time. That's part of our game and we don't want it to be forbidden.