20140101 (Wednesday, 01 January 2014)¶
Why the new apps management in Django 1.7 is not enough¶
(Note: the following may not be true anymore. See future posts and https://www.lino-framework.org/dev/apps.html)
Remember 2013-12-26 when I was delighted to discover Aymeric’s new app management, but then discovered that it has a serious difference: AppConfig objects don’t get instantiated before importing the models modules, but at the same time. Which for me made it impossible to use Aymeric’s implementation for doing the things I need to do.
Today during a chat with Joe I grew new hope that maybe I was wrong, that maybe it is possible?
But after some meditation I –unfortunately– came again to the same conclusion: the new apps management in Django 1.7 is not enough for me. Here is an example which explains why.
Let’s take Renamed “Lino Faggio” to “Lino Voga”. It uses Lino’s standard calendar module
lino.apps.cal
, but extends the Room model defined there:
it adds two fields
it adds another base class (the
ContactRelated
mixin)it overrides the __unicode__ method
Here is the relevant application developer’s code which defines the
Faggio version of cal.Room
:
from lino.apps.cal.models import Room
from lino.modlib.contacts.models import ContactRelated
class Room(Room, ContactRelated):
tariff = dd.ForeignKey('products.Product', ...)
calendar = dd.ForeignKey('cal.Calendar', ...)
def __unicode__(self):
s = dd.BabelNamed.__unicode__(self)
if self.company and self.company.city:
s = '%s (%s)' % (self.company.city, s)
return s
For this to work, the library version of cal.Room
must have abstract=True. Here is how
that class is begin defined:
class Room(dd.BabelNamed):
class Meta:
abstract = settings.SITE.is_abstract_model('cal.Room')
verbose_name = _("Room")
verbose_name_plural = _("Rooms")
The abstractness of certain models in a library app must be optional. Not all Lino applications who use the calendar module want to override the Room model. Afaics there is no way in Django to make a model abstract “afterwards”. IOW we need a central place where models modules can ask whether it wants a given model to be abstract or not.
This is why we call the is_abstract_model
method. The
implementation of this method has evolved in time. The first
implementation used a simple set of strings in a class attribute of
djangosite.Site. That might have been a standard Django setting.
But as things got more and more complex, it became difficult to define
this manually. And it was useless work because every app does know
which library models it is going to override. But how to load that
information from an app before actually importing it? I then
discovered that Django doesn’t use the __init__.py
files of
installed apps. And of course I was lucky to have a djangosite.Site
class which is being instantiated before settings have finished to
load…
The trick here is that the lino_faggio/cal/__init__.py
file
now contains this information in the extends_models attribute:
class Plugin(Plugin):
extends = 'lino.apps.cal'
extends_models = ['cal.Event', 'cal.Room']
This is an important pattern used by Lino’s “modlib” (a collection of reusable apps designed to work together.).
And I don’t see how it would be possible to do this using the current “plain Django 1.7” implementation. Or maybe I have a mind lock;-)
Note that I am in the process of renaming lino.modlib to
lino.apps, and the cal
module has already moved, contacts
not yet.