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

jinja : Use the Jinja template engine

This document describes the lino.modlib.jinja plugin.

This page contains code snippets (lines starting with >>>), which are being tested during our development workflow. The following snippet initializes the demo project used throughout this page.

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

Plugin settings

Two settings to used by the local.css file.

lino.modlib.jinja.tile_width

The width of each tile for tables in display mode “tiles”.

Default value is "20em".

lino.modlib.jinja.background_color

The background color of tiles for tables in display mode “tiles” and certain other elements. Default value is "Gainsboro".

The local.css file

This file contains CSS available in all front ends.

Build methods

This module adds a build method for lino.modlib.printing.

class lino.modlib.jinja.JinjaBuildMethod

Inherits from lino.modlib.printing.DjangoBuildMethod.

Model mixins

class lino.modlib.jinja.XMLMaker

Usage example in Lino and XML

xml_file_name

The name of the XML file to generate. This file will be overwritten without asking. The name formatted with one name self in the context.

xml_file_template

The name of a Jinja template to render for generating the XML content.

This must be either None or a Path object.

xml_validator_file

The name of a “validator” to use for validating the XML content.

This must be either None or a Path object.

Lino choose the validation method from the file’s suffix. It currently can handle suffixes “.xsd” and “.sch”.

get_xml_file()

Get the name of the XML file to be generated for this database row.

Returns an instance of lino.utils.media.MediaFile.

make_xml_file(ar)

Make the XML file for this database row.

django-admin commands

This plugin defines two django-admin commands.

showsettings

Print to stdout all the Django settings that are active on this Lino site.

Usage example:

>>> from atelier.sheller import Sheller
>>> shell = Sheller("lino_book/projects/min1")
>>> shell("python manage.py showsettings | grep EMAIL")
...
DEFAULT_FROM_EMAIL = webmaster@localhost
EMAIL_BACKEND = django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST = mail.example.com
EMAIL_HOST_PASSWORD =
EMAIL_HOST_USER =
EMAIL_PORT = 25
EMAIL_SSL_CERTFILE = None
EMAIL_SSL_KEYFILE = None
EMAIL_SUBJECT_PREFIX = [min1]
EMAIL_TIMEOUT = None
EMAIL_USE_LOCALTIME = False
EMAIL_USE_SSL = False
EMAIL_USE_TLS = False
SERVER_EMAIL = root@localhost
status

Write a diagnostic status report about this Lino site.

A functional replacement for the diag command.

>>> shell = Sheller("lino_book/projects/min1")
>>> shell("python manage.py status")
...
Plugins
=======

- lino : lino
- about : lino.modlib.about
- jinja : lino.modlib.jinja
- extjs : lino.modlib.extjs
- system : lino.modlib.system
- users : lino.modlib.users
- office : lino.modlib.office
- xl : lino_xl.lib.xl
- countries : lino_xl.lib.countries
- contacts : lino_xl.lib.contacts
- staticfiles : django.contrib.staticfiles
- sessions : django.contrib.sessions

Config directories
==================

- .../lino_xl/lib/contacts/config
- .../lino/modlib/users/config
- .../lino/modlib/extjs/config
- .../lino/modlib/jinja/config
- .../lino/config

The output may be customized by overriding the jinja/status.jinja.rst template.

jinja/status.jinja.rst

The template file used by the status command.

The Jinja template context

The context returned by ar.get_printable_context contains a series of names that a server administrator can use in local templates. These names are simple shortcuts to their respective functions imported from well-known places:

Here is a snippet that show them all:

>>> ar = rt.login()
>>> sorted(ar.get_printable_context().keys())
['Avg', 'Count', 'E', 'F', 'Max', 'Min', 'Sum', '_', 'activate_language', 'ar',
'bool2js', 'bool2text', 'dd', 'decfmt', 'fdf', 'fdl', 'fdm', 'fdmy', 'fds',
'getattr', 'iif', 'now', 'parse', 'pgettext', 'requested_language', 'restify',
'rt', 'tostring', 'unicode']
>>> pprint(ar.get_printable_context())
{'Avg': <class 'django.db.models.aggregates.Avg'>,
 'Count': <class 'django.db.models.aggregates.Count'>,
 'E': <lxml.builder.ElementMaker object at ...>,
 'F': <class 'django.db.models.expressions.F'>,
 'Max': <class 'django.db.models.aggregates.Max'>,
 'Min': <class 'django.db.models.aggregates.Min'>,
 'Sum': <class 'django.db.models.aggregates.Sum'>,
 '_': <function gettext at ...>,
 'activate_language': <function activate at ...>,
 'ar': <lino.core.requests.BaseRequest object at ...>,
 'bool2js': <function BaseRequest.get_printable_context.<locals>.<lambda> at ...>,
 'bool2text': <function bool2text at ...>,
 'dd': <module 'lino.api.dd' from '.../lino/api/dd.py'>,
 'decfmt': <bound method Site.decfmt of <lino_book.projects.min1.settings.Site object at ...>>,
 'fdf': <function fdf at ...>,
 'fdl': <function fdl at ...>,
 'fdm': <function fdm at ...>,
 'fdmy': <function fdmy at ...>,
 'fds': <function fds at ...>,
 'getattr': <built-in function getattr>,
 'iif': <function iif at ...>,
 'now': datetime.datetime(2014, 10, 23, ...),
 'parse': <function BaseRequest.get_printable_context.<locals>.parse at ...>,
 'pgettext': <function pgettext at ...>,
 'requested_language': 'en',
 'restify': <function restify at ...>,
 'rt': <module 'lino.api.rt' from '.../lino/api/rt.py'>,
 'tostring': <function tostring at ...>,
 'unicode': <class 'str'>}

The parse function

One of these names is parse(), which deserves some attention:

lino.modlib.jinja.parse(s)

Parse the given string using the current template context.

Let’s illustrate this parse() function in a simplified context:

>>> ctx = dict(x=1, y=2, a="A")
>>> def test(s):
...     print(dd.plugins.jinja.renderer.parse(s, **ctx))
>>> test("Hello, world")
Hello, world
>>> test("x is {{x}} and a is {{a}}")
x is 1 and a is A

But what if we want to insert a string that is configurable via a jinja template in the site settings? The following won’t do what we expect:

>>> ctx.update(footer_content="x is {{x}} and a is {{a}}")
>>> test("{{footer_content}}")
x is {{x}} and a is {{a}}

That’s why the template context also contains a parse() function. The following simulates what Lino does, for illustration:

>>> def parse(s):
...     return dd.plugins.jinja.renderer.parse(s, **ctx)
>>> ctx.update(parse=parse)
>>> test("{{parse(footer_content)}}")
x is 1 and a is A

Edge cases

Parsing an empty string returns an empty string:

>>> test('')

The following exception was added for #6643 (When Jinja says “Can’t compile non template nodes”):

>>> test(1)
Traceback (most recent call last):
...
Exception: 1 is not a string (context is {...})
>>> s = 1
>>> dd.plugins.jinja.renderer.jinja_env.from_string(s).render(**ctx)
Traceback (most recent call last):
...
TypeError: Can't compile non template nodes