Optimizing RolesByCompany caused an avalanche¶
December 27-29, 2023
I did some fundamental cleanup in the following methods. Initially it was just for #5346 (Optimize RolesByCompany), which was trivial: there was no insert layout and therefore it was difficult to add a new contact person for an organization.
But working on this caused an avalanche of #5353 (Use mark_safe instead of ElementTree).
ElementTree versus mark_safe()¶
The fundamental new thing is that we started to use mark_safe()
function.
When a virtual field or any function returns some content, then the caller needs
to know whether that content is (1) plain text or (2) HTML-formatted text.
During the first years of Lino, I discovered ElementTree and fell in love. And I
still say that it’s a great tool for generating HTML. And I based most function
calls on the convention that when it’s a str then it is plain text (and thus
needs to get escaped when rendered as HTML), and when a function wants to return
formatted text, then it must return an ElementTree.
But this convention is suboptimal when we return content of a rich text field,
it that is already a chunk of HTML, verified and bleached and validated. In that
case a method that is expected to return ElementTree currently has to parse the
stored HTML using lxml.html.fromstring()
in order to get an ET from it.
Which is a bit stupid because that ET is used for nothing else but to generate
HTML.
I had been feeling this issue for quite some time already, but now I finally created a separate ticket for it: #5353 (Use mark_safe instead of ElementTree).
qs2summary() no longer returns an etree but an html string. As a result,
Actor.summary_sep
no longer needs to be a callable, we can remove the “from lino.core.utils import comma”.The default implementation of
Model.as_story_item()
returnedas_summary_row()
but now returnsas_paragraph()
summary_row()
replaced byas_summary_row()
row_as_summary()
ar.obj2htmls(self)
Model.as_search_item()
About mark_safe()¶
The Django docs about mark_safe() say that a “safe” string becomes unsafe as soon as it is modified. I wondered what happens if you just concatenate two safe strings. Answer the result is still safe.
>>> from django.utils.html import mark_safe
>>> s1 = mark_safe("<b>Foo</b>")
>>> s2 = mark_safe("<b>Bar</b>")
>>> s3 = mark_safe("<em>{} and {}</em>")
>>> type(s1+s2)
<class 'django.utils.safestring.SafeString'>
But already the following requires another call to mark_safe()
:
>>> s4 = s3.format(s1, s2)
>>> type(s4)
<class 'str'>
Note that the word “safe” here has nothing to do with actual security. A
SafeString
can contain dangerous HTML.