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, pm showsettings and pm status.

pm 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
pm 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

Mail settings
=============

- ADMINS: []
- 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
-s
--sendto

You can send this report by email using --sendto or -s.

>>> shell("python manage.py status --help")
...
usage: manage.py status [-h] [-b] [-s RECIPIENT] [--version] [-v {0,1,2,3}]
                        [--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback]
                        [--no-color] [--force-color] [--skip-checks]

options:
  -h, --help            show this help message and exit
  -b, --batch, --noinput
                        Do not prompt for input of any kind.
  -s RECIPIENT, --sendto RECIPIENT
                        Also send the report as email to recipient.
  --version             Show program's version number and exit.
    -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g. "myproject.settings.main".
                        If this isn't provided, the DJANGO_SETTINGS_MODULE
                        environment variable will be used.
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".
  --traceback           Display a full stack trace on CommandError exceptions.
  --no-color            Don't colorize the command output.
  --force-color         Force colorization of the command output.
  --skip-checks         Skip system checks.

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