Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

Dynamic models

This document collects ideas on one possible way for working around inject_field (#246)

General stuff:

>>> from lino import startup
>>> startup('lino_book.projects.min9.settings')
>>> from lino.api.doctest import *

BTW: The dynamic-models project confirms that it is possible.


We will add a new attribute dynamic_models for lino.core.plugin.Plugin. This is a list or tuple of strings, each string is of the form "<app_label>.<ModelName>" and specifies a model whose class object should get dynamically factored.

When the Site object instantiates (i.e. before Django sees the settings) it will collect the dynamic_models of all plugins and create dynamic class objects for each of them. These class objects inherit from all mixins found in all installed plugins.

With at least one gotcha: these mixins must get defined in a module called bases.py. That’s because we cannot import any models.py file before

Models that are candidates for dynamic factoring must use the is_abstract_model <lino.core.site.Site.is_abstract_model> method when declaring their inner Meta class. But then?



class Plugin(ad.Plugin):


class SiteConfigBase(dd.Model):

    class Meta:
        abstract = True
        verbose_name = _("Site configuration")


from .mixins import *

SiteConfig = dd.dynamic_model(SiteConfigBase)


class Plugin(ad.Plugin):


from lino.modlib.system.mixins import SiteConfigBase

class OtherSiteConfig(SiteConfigBase):

    class Meta:
        abstract = True


from .mixins import *

But how to implement dd.dynamic_model(SiteConfigBase)? The challenge is that Lino must discover all abstract models which inherit from the given class.

Abstract models are not stored in the models cache:

>>> from django.apps import apps
>>> [m for m in apps.get_models() if m._meta.abstract]

And anyway the models cache is ready only when all models modules have been imported. Which means that it is not easy.