Chapter 13 · Templates

13.10 Libraries: Library, custom tags and {% load %}

A library is a Library, a dictionary of tags and one of filters, kept in a module’s variable "register". The DjangoTemplates backend imports every such module of django.templatetags and of the installed applications when it is made, {% load %} copies a library into the parser of the one template being compiled, and simple_tag, simple_block_tag and inclusion_tag make a tag of an ordinary function.

The parser knows a tag or a filter only by the function its tables hold under that name (From text to nodes: the lexer and the parser). Libraries fill those tables, in one of two ways. A builtin library is added to every parser as it is made, so its tags and filters are in every template. Every other library the engine knows by a name, and it reaches a template only when that template says {% load %} with the name.

Registering tags and filters on a Library

Library holds two dictionaries, Library.tags and Library.filters, and its methods are ways of filling them. A module becomes a library by keeping one in a variable named "register", the one name Django looks for: import_library imports a dotted path and returns the module’s "register", and raises InvalidTemplateLibrary when the import raises an ImportError or the variable is missing.

# fleet/templatetags/fleet_tags.py
from django import template

register = template.Library()


@register.filter
def knots(value):
    return f"{value} kn"


@register.simple_tag
def berth(port, number):
    return f"{port.name}, berth {number}"

Library.tag registers a compile function as it is, under the name it is given or the function’s own. Library.filter does the same for a filter, and copies the flags it is given, is_safe=True, needs_autoescape=True or expects_localtime=True, onto the function, where FilterExpression.resolve reads them (Variables and filters: FilterExpression and Variable). It sets each twice, on the function registered and on the innermost function under its decorators, and the comment there says why: a decorator such as stringfilter, which turns a filter’s first argument into a string, reads the flag from the innermost function.

How an engine finds its libraries

DjangoTemplates.__init__ gathers the libraries before it makes its Engine. get_template_tag_modules looks in django.templatetags and then in the templatetags package of each installed application, in the order of INSTALLED_APPS. In each package it imports every module, subpackages included, and keeps those with a "register"; a module that cannot be imported stops the backend from being made, an import error raised again as InvalidTemplateLibrary. A library’s name is its module’s path after the package: "fleet_tags" for the module above, "charts.maps" for a module in a subpackage. get_installed_libraries makes a dictionary of the names and paths, and DjangoTemplates.get_templatetag_libraries updates it with the "libraries" option, so a name given there replaces an installed module’s.

Engine.__init__ then imports them all, whether a template ever loads them or not: Engine.template_libraries maps each name to its Library, and Engine.template_builtins lists the three of Engine.default_builtins and then those of the "builtins" option. An Engine made outside a backend knows by name only the libraries it is handed.

{% load %}: a library enters one parser

A parser is made for each compile with the engine’s libraries and builtins. It copies each builtin into its tables at once, in order, with Parser.add_library, a dictionary update, so a later library replaces an earlier one’s tag or filter of the same name; the others it keeps by name, as Parser.libraries.

{% load %} does all its work in its compile function, load. It splits the tag on white space, so the names in it are words, never variables, looks each up with find_library, and hands the library to Parser.add_library; an unknown name is a TemplateSyntaxError that lists every name the engine knows. In the form {% load berth knots from fleet_tags %}, load_from_library first builds a new Library of only the tags and filters named. The LoadNode that load returns renders the empty string: by then the tag has done all it does.

The tables are read as each token is compiled, so what {% load %} brings is in force from that token to the end of the text, and nowhere else:

Tags made from functions

A tag written by hand is a compile function and a node. simple_tag, simple_block_tag and inclusion_tag write both for a function that only computes something, and return the function unchanged, so Python can still call it.

When the decorator runs, as its module is imported, it reads the function’s signature. With takes_context=True the first parameter must be "context", and it is struck from the list the template will fill; a function without one is refused there with a TemplateSyntaxError.

When a template is compiled, that compile function splits the token, takes off a final "as" and a name where the tag allows one, and matches the rest against the signature with parse_bits. A word name=value is a keyword argument, read by token_kwargs, and any other word a positional one. Each value is compiled as {{ }} compiles one, filters included. The arguments are checked here, once, and an argument the signature does not admit, or a required one left out, is a TemplateSyntaxError: {% berth port %} fails with "'berth' did not receive value(s) for the argument(s): 'number'".

When the template renders, the node resolves each argument in the context and calls the function, with the context itself first under takes_context=True.

simple_tag

SimpleNode.render returns what the function returned, passed through conditional_escape when the context autoescapes: with a ship named <Beagle>, {% berth ship 4 %} renders "&lt;Beagle&gt;, berth 4", so markup must come back safe, from format_html for instance (Autoescaping and safe strings). With "as" and a name, the value is set, unescaped, in the context’s top dictionary, and the tag renders the empty string; set inside {% for %}, it is gone after {% endfor %}.

simple_block_tag

simple_block_tag makes a tag with a body. The function’s first parameter must be "content", or its second after "context". The compile function parses the body up to the end tag, {% endnotice %} for {% notice %} unless end_name= names another, and keeps its nodes. At each render the body is rendered in the same context and passed as the content; the rest is SimpleNode.render, escaping included.

inclusion_tag

inclusion_tag takes a template as well as the function, which returns a dictionary for it. InclusionNode.render calls the function, fetches a template given by name or by a list of names from the engine of the template being rendered, and keeps it in the render context under the node, so that inside {% for %} it is fetched once for each render of the template around it, not once a turn.

It renders in a context made by BaseContext.new, holding only the returned dictionary: none of the variables around the tag, and none of the context processors’ values (The context: Context, RequestContext and context processors). The one exception is "csrf_token", copied across when the outer context has it, because, the comment there says, inclusion tags are often used for forms and CSRF protection should be as simple as possible to use.