Thursday, March 6, 2025¶
I’m working on #5964 (pm prep in avanti1 says ‘str’ object has no attribute ‘get_layout_handle’)
In lino_xl/lib/courses/models.py
we had:
@dd.requestfield(_("Enrolments"))
def enrolments(self, ar):
return self.get_enrolments(start_date=dd.today())
def get_enrolments(self, **pv):
return rt.models.courses.EnrolmentsByCourse.create_request(self,
param_values=pv)
The issue went away when I added “if ar is None: return” in the enrolments
virtual field before calling get_enrolments()
.
But why? And why did it start only now?
And the same error message happens when I say doctest
docs/specs/avanti/courses.rst
, this time it is when
courses/Course/presence_sheet.weasy.html
says:
{% for enrolment in obj.get_enrolments(
start_date=ar.action_param_values.start_date,
end_date=ar.action_param_values.end_date) %}
Note that get_enrolments()
accesses
rt.models.courses.EnrolmentsByCourse
. This is an actor, more
precisely a table. And avanti redefines this table.
Lino is trying to setup the actor handle for the EnrolmentsByCourse
from lino_xl.lib.courses
instead of lino_avanti.lib.courses
.
Aha! Explanation: It’s an aftermath of #5956 (python-tools no longer works). One of those automatic formatters that came with pulsar-ide-python, which I installed to replace the orphaned python-tools, insists on moving all global imports to the top of the file. Including these ones:
from .ui import *
But these imports must come at the end of the models.py
file, at
least for lino_avanti.lib.courses
.
No. They don’t need to be at the end of models.py
file, it’s enough if
they are after the:
from lino_xl.lib.courses.models import *
Should we stop using from .ui import *
?¶
Most people say that from x import *
is bad code style. But we use from
.ui import *
as a feature in a well-defined context in order to follow another
code style principle which is “don’t repeat yourself”. We deliberately merge
database models and actors into a same pot (the plugin’s namespace) because it
makes documentation more concise. For application developers it makes no sense
to differentiate between models, tables, choicelists because these are
implementation details. But the more I think about this I realize that it’s a
controversial topic, and for a developer who is used to relying on their IDE it
can be a serious obstacle for diving into Lino.
I started to move all from .ui import *
lines from the bottom of the
models.py
file to its top. But actually I have no clear plan yet. Even
if we would name them explicitly, pyflakes would still complain because we
import them and then don’t use them.
There is another topic, related to this: we actually have two namespaces per
plugin: dd.plugins.foo
and rt.models.foo
. Their difference is that
dd.plugins
is available as soon as the plugins have been installed while
rt.models
is available only when Django has populated its database models.
The items of lino.api.dd.plugins
are instances of
lino.core.plugins.Plugin
while the items of lino.api.rt.models
are the models.py
module objects.
Once upon a time we had the actors collected into a namespace rt.actors
,
separately from rt.models
. This used a automagic behaviour that we copied
from Django: Django, when starting up, loops over INSTALLED_APPS
and
tries to import a module appname + ".models"
.
I had a look at django.apps.registry
and I am tempted to try to review
the Lino startup process because Django has evolved during the last 10 years.
Let lino.core.plugin.Plugin
inherit from AppConfig
and use
apps.get_app_config()
instead of dd.plugins
. Override
AppConfig.import_models()
so that it also tries to import a “ui” module,
similar to what it does with a “models” module.
That would be fun! I’m really tempted. But there are more urgent things to do right now.