Jinja template configuration in Django
The first step to use Jinja in
Django is to install the core package with the pip
command: python3 -m pip install Jinja2
. Note the package name is Jinja2
while the latest Jinja version is 3.0.2
. This is an interesting naming convention, since Jinja 1 used the package name Jinja
, Jinja 2 used the package name Jinja2
and when moving toward a version greater than 3 the Jinja package name remained Jinja2
.
Next, you need to configure Jinja
in a Django project inside the settings.py
file.
Listing 4-1 illustrates a basic Jinja configuration for Django.
Listing 4-1. Jinja configuration in Django settings.py
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent PROJECT_DIR = Path(__file__).resolve().parent TEMPLATES = [ { 'BACKEND':'django.template.backends.jinja2.Jinja2', 'DIRS': [ PROJECT_DIR / 'jinjatemplates' ], 'APP_DIRS': True, }, { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
As you can see in listing 4-1,
there are two configurations declared in the TEMPLATES
variable: a
dictionary for the Jinja template configuration with 'BACKEND':'django.template.backends.jinja2.Jinja2'
and another dictionary with 'BACKEND': 'django.template.backends.django.DjangoTemplates'
that represents the default Django template configuration. Since Django
templates are still used by things like the Django admin and many
third party packages, I highly recommended you use the base
configuration in listing 4-1, as this guarantees all kinds of templates (i.e. Django and Jinja) are supported in a project, specially apps you have no control over which kind of templates they operate with.
The Jinja configuration in
listing 4-1 is one of the most basic possible. In this case, the
BACKEND
variable uses the
django.template.backends.jinja2.Jinja2
value to
activate Jinja templates, and is followed immediately with the
DIRS
and APP_DIRS
variables which tell
Django where to locate Jinja templates.
Template search paths
The APP_DIRS
variable permits the look-up of templates inside special app
sub-directories named jinja2
. This is helpful if you
wish to contain Jinja templates to apps, but be aware the template
search path is not aware of app namespaces. For example, if you
have two apps that both rely on a template named
index.html
-- as illustrated in listing 4-2 -- and
both app's have a method in views.py
that returns
control to the index.html
template (e.g.
render(request,'index.html')
), both apps will use the
index.html
from the top-most declared app in
INSTALLED_APPS
, so one app won't use the expected
index.html
.
Listing 4-2. Django apps with jinja2 dirs with potential conflict and namespace qualification
# Templates directly under jinja2 folder can cause loading conflicts +---+-<PROJECT_DIR_project_name_conflict> | +-asgi.py +-__init__.py +-settings.py +-urls.py +-wsgi.py | +-about(app)-+ | +-__init__.py | +-models.py | +-tests.py | +-views.py | +-jinja2-+ | | | +-index.html +-stores(app)-+ +-__init__.py +-models.py +-tests.py +-views.py +-jinja2-+ | +-index.html # Templates classified with additional namespace avoid loading conflicts +---+-<PROJECT_DIR_project_name_namespace> | +-asgi.py +-__init__.py +-settings.py +-urls.py +-wsgi.py | +-about(app)-+ | +-__init__.py | +-models.py | +-tests.py | +-views.py | +-jinja2-+ | | | +-about-+ | | | +-index.html +-stores(app)-+ +-__init__.py +-models.py +-tests.py +-views.py +-jinja2-+ | +-stores-+ | +-index.html
To fix this potential conflict,
the recommended practice is to add an additional sub-folder to act
as a namespace inside each jinja2
directory as
illustrated in the second set of folders in listing 4-2. In this
manner, you can then re-direct control to a template using this
additional namespace sub-folder to avoid any ambiguity. So to send
control to the about/index.html
template you would
declare render(request,'about/index.html')
and to send
control to the stores/index.html
you would declare
render(request,'about/index.html')
.
If you wish to disallow this
behavior of allowing templates to be loaded from these internal app
sub-folders, you can do so by setting APP_DIRS
to
FALSE
.
A more common approach for Jinja
templates is to have a single folder or various folders -- that
live outside app structures -- to hold Jinja templates. Django
first looks for a matching Jinja templates in the first
DIRS
value and then in jinja2
folders in
apps -- if APP_DIRS
is TRUE
-- until it
either finds a matching template or throws a
TemplateDoesNotExist
error.
For the case illustrated in
listing 4-1, the only DIRS
value relies on a directory
named jinjatemplates
relative to a path determined by
the PROJECT_DIR
variable. This variable technique is
helpful when deploying a Django project across different machines,
because the path is relative to the top-level Django project
directory (i.e. where the settings.py
and main
urls.py
file are) and adjusts dynamically irrespective
of where a Django project is installed (e.g.
/var/www/
, /opt/website
,
C://website/
).
Similar to the Django template
OPTIONS
variable, Jinja also supports a series of
customizations through the OPTIONS
variable. In the
case of Jinja, the OPTIONS
variable is a dictionary of
key-values that correspond to Jinja environment initialization
parameters[3]
By default, Django internally
sets a series of Jinja environment initialization parameters to
align Jinja's template behavior with that of Django templates.
However, you can easily override these settings with the
OPTIONS
variable. The next sections describe these
important settings.
Auto-escaping behavior
Django enables Jinja template auto-escaping by default, a behavior that's actually disabled in the Jinja engine in its out-of-the-box state. The crux of auto-escaping is that, on the one hand it errs on the side of precaution and security -- limiting the possibility to mangle output or introduce XSS (Cross-site scripting) vulnerabilities in HTML -- but on the other hand, it also introduces extra processing in the template engine that can cause performance problems.
By default, Django templates
auto-escape all output from template variables -- <
is converted to <
,>
is
converted to >
,'
(single quote)
is converted to '
,"
(double
quote) is converted to "
and
&
is converted to & --
unless
you explicitly disable this behavior. Jinja in its out-of-the-box
state doesn't auto-escape anything and you need to explicitly tell
it when you want to auto-escape something.
Because the Jinja template
integration for Django was done by Django designers, Jinja
auto-escaping is enabled, to err on the side of security, just like
it's for Django templates. However, you can disable Jinja
auto-escaping with the autoescape
parameter in
OPTIONS
as illustrated in listing 4-3.
Listing 4-3. Jinja disable autoescaping in Django
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent PROJECT_DIR = Path(__file__).resolve().parent TEMPLATES = [ { 'BACKEND':'django.template.backends.jinja2.Jinja2', 'DIRS': [ PROJECT_DIR / 'jinjatemplates' ], 'APP_DIRS': True, 'OPTIONS': { 'autoescape': False }, } ]
As you can see in listing 4-3,
autoescape
is assigned False
and with
this change Jinja templates behave just as Jinja designers intended
(i.e. you need to explicitly check where auto-escaping is necessary
vs. the Django template way to check where auto-escaping isn't
necessary).
Auto-reload template behavior & caching
In its out-of-the-box state, Jinja's template loader checks every time a template is requested to see if the source has changed, if it has changed Jinja reloads the template. This can be helpful in development where a template's source changes constantly, but can also translate into a performance hit in production where a template's source rarely changes and the check incurs in a delay.
By default, the Django framework
Jinja integration takes a sensible approach and enables Jinja
template auto-reloading based on the DEBUG
variable in
settings.py
. If DEBUG=True
-- a common
setting in development -- Jinja template auto-reloading is set to
True
and if DEBUG=False
-- a common
setting in production -- Jinja template auto-reloading is set to
False
. Nevertheless, you can explicitly set Jinja's
auto-loading behavior with the auto_reload
parameter
in OPTIONS
.
The Jinja engine by default also
caches up to four hundred templates. This means that when template
four hundred and one is loaded, Jinja cleans out the least recently
used template, the latter of which must be reloaded from its origin
again if required at a later time. The Jinja cache limit can be
adjusted with the cache_size
parameter in
OPTIONS
(e.g. cache_size=1000
, to set a
one thousand template cache). Setting cache_size
to 0
(zero) disables caching and setting cache_size
to -1
enables unlimited caching.
Another caching mechanism
available in Jinja templates is byte-code caching. When you create
Python source files (i.e. those with .py
extensions)
Python produces mirror like files with .pyc
extensions
that contain byte-code. Generating these byte-code files takes
time, but they're a natural part of Python's run-time process.
Jinja templates being based on Python also need to be turned into
byte-code, but it's a process you can customize with the
bytecode_cache
parameter in OPTIONS
.
The bytecode_cache
parameter can be assigned either a custom byte-cache[4] or one of Jinja's built-in byte-code
caches, which include support for standard file-system caching or
more specialized caching with memcached.
Invalid template variables
You can set various behaviors
when an invalid variable is encountered in Jinja templates. Django
sets Jinja with two default behaviors, one for when
DEBUG=True
-- a common setting in development -- and
the other for when DEBUG=False
-- a common setting in
production.
If DEBUG=True
and an
invalid variable is set in a Jinja template, Jinja uses the
jinja2.DebugUndefined
class to process it. The
jinja2.DebugUndefined
class outputs the variable
verbatim for rendering (e.g. if the template has the
{{foo}}
statement and the variable doesn't exist in
the context, Jinja outputs {{foo}}
, making it easier
to spot an invalid variable).
If DEBUG=False
and
an invalid variable is set in a Jinja template, Jinja uses the
jinja2.Undefined
class to process it. The
jinja2.Undefined
class outputs a blank space in the
position of the variable for rendering (e.g. if the template has
the {{bar}}
statement and the variable doesn't exist
in the context, Jinja outputs a blank space). It's worth mentioning
this last behavior aligns with the default behavior of invalid
variables in Django templates.
In addition to the
jinja2.DebugUndefined
and
jinja2.Undefined
classes, Jinja also supports the
jinja2.StrictUndefined
class. The
jinja2.StrictUndefined
class is used to generate an
immediate error instead of proceeding with rendering, which is
helpful for quicker diagnosis of invalid variables. However, be
aware this last class changes its behavior based on the
DEBUG
variable, it either generates a stack error with
the invalid variable name (i.e. when DEBUG=True
) or it
generates a standard HTTP 500 error page (i.e. when
DEBUG=False
).
Listing 4-4 illustrates how to
configure a Jinja class to handle invalid variables through the
OPTIONS
parameter in settings.py
.
Listing 4-4. Generate error for invalid variables in Jinja with jinja2.StrictUndefined
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent PROJECT_DIR = Path(__file__).resolve().parent import jinja2 TEMPLATES = [ { 'BACKEND':'django.template.backends.jinja2.Jinja2', 'DIRS': [ PROJECT_DIR / 'jinjatemplates' ], 'APP_DIRS': True, 'OPTIONS': { 'undefined':jinja2.StrictUndefined }, } ]
As you can see in listing 4-4, we
first declare import jinja2
to gain access to Jinja's
classes in settings.py
. Next, we declare the
undefined
key inside the OPTIONS
parameter and assign it the Jinja class to process invalid
variables. In this case, we use the
jinja2.StrictUndefined
class to get errors when
invalid templates variables are encountered, but you could equally
use any of the other two Jinja classes to handle invalid variables
(i.e. jinja2.DebugUndefined
or
jinja2.Undefined
).
Template loaders
Jinja template loaders are Python
classes that implement the actual logic required to search &
load templates. Earlier in the section Template search paths, I
described how Jinja searches for templates using the
DIRS
and APP_DIRS
variables which are
part of Django's template configuration. However, I intentionally
omitted a deeper aspect associated with this template search
process: each search mechanism is backed by a template loader.
In most circumstances, you won't
need to deal with Jinja template loaders, since Jinja loaders are
taken care of in the background by simply relying on the
DIRS
and APP_DIRS
variables. But if you
need to load Jinja templates from somewhere else than these
locations (e.g. from an in-memory structure or a database) you can
specify template loaders with the loader
key inside
the OPTIONS
parameter.
Like Django template loaders, Jinja also offers the ability to create custom template loader[5], in addition to using built-in Jinja template loaders similar to those offered by Django template (e.g. loading templates from a Python dictionary).
Tip You can set custom values for any Jinja environment initialization parameter[6] in OPTIONS. The prior sections are just four of the most common Jinja template parameters, later sections describe other available OPTIONS.
Note OPTIONS is only intended for Jinja environment initialization parameters, other Jinja environment settings require configuring a separate Jinja environment class (e.g. Jinja globals, Jinja custom filters & tests and Jinja policies).