===================================== 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 :lino:`/dev/apps`) Remember :blogref:`20131226` 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 :ref:`faggio`. It uses Lino's standard calendar module :mod:`lino.apps.cal`, but extends the `Room` model defined there: - it adds two fields - it adds another base class (the :class:`ContactRelated ` mixin) - it overrides the `__unicode__` method Here is the relevant application developer's code which defines the *Faggio* version of :class:`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 :class:`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 :setting:`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 :xfile:`__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 :file:`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.