Introduction
django-jinja is a BSD Licensed, simple and non-obstructive jinja2 backend for Django.
Rationale
Jinja2 provides certain advantages over the native system of Django, for example, explicit calls to callable from templates, has better performance and has a plugin system, etc …
Django comes with a jinja backend, why should I use django-jinja?
The Django builtin backend has a very limited set of features if we compare it with the django template engine and in my opinion is not very usable because it does not integrate well with the rest of django such as its filters, template tags and preloading of templatetags, among others.
django-jinja comes to the rescue and adds everything missing. This is a brief list of differences with django’s built-in backend:
-
Auto-load templatetags compatible with Jinja2 the same way as Django.
-
Find the templates as usual in
"<appname>/templates"
directory instead of"<appname>/jinja2"
directory (you can overwrite this behavior, see below). -
Django templates can coexist with Jinja2 templates without any problems. It works as middleware, intercepts Jinja templates by file path pattern.
-
Django template filters and tags can mostly be used in Jinja2 templates.
-
I18n subsystem adapted for Jinja2 (makemessages now collects messages from Jinja templates, respects the
ext.i18n.trimmed
policy) -
jinja2 bytecode cache adapted for using django’s cache subsystem.
-
Support for django context processors.
Note
|
The usage of context processors is available for compatibility and migrations, but creating new context processors is not the recommended way to set global context. With django-jinja, you can do this by setting global data or global constants. See below, in the "Custom filters, globals, constants and tests" section. |
Requirements
-
Python >= 3.8
-
Django >= 3.2
-
jinja2 >= 3.0
If you are using older versions of Django or Python, you need an older version of django-jinja:
django-jinja | supported python versions | supported django versions |
---|---|---|
==1.4.2 |
2.7, 3.3, 3.4 |
1.5, 1.6, 1.7, 1.8 |
==2.4.1 |
2.7, 3.4, 3.5 |
1.8, 1.11 |
==2.6.0 |
3.5 (not django 3.0), 3.6, 3.7, 3.8 |
1.11, 2.2, 3.0 |
>=2.7.0 |
3.5 (django 2.2 only), 3.6, 3.7, 3.8, 3.9 |
2.2, 3.0, 3.1, 3.2 |
>=2.8.0 |
3.6, 3.7, 3.8, 3.9 |
2.2, 3.0, 3.1, 3.2 |
>=2.10.0 |
3.6, 3.7, 3.8, 3.9, 3.10 |
2.2, 3.2, 4.0 |
>=2.11.0 |
3.8, 3.9, 3.10, 3.11 |
3.2, 4.0, 4.1, 4.2 |
Quick Start
Add it to Django’s installed apps list:
INSTALLED_APPS += ('django_jinja',)
Followed by the basic template engine configuration:
TEMPLATES = [
{
"BACKEND": "django_jinja.jinja2.Jinja2",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {}
},
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {}
},
]
Note
|
If you are using the default value for the app templates directory of the django-jinja backend, take care of the template engines order, because the django-jinja backend by default uses the same directory for the templates as the django template engine. If you put the django engine first every jinja template will be found by the django engine. |
To read more on the logic of the DIRS
and APP_DIRS
settings,
and how the engines resolve template paths, check out
Django’s section on setting up template engines.
User Guide
Using Jinja2 templates in your views
By default, django-jinja's template backend matches files with the extension .jinja
,
and (if using the APP_DIRS
template loader) it crawls the same templates
folders
within your apps as the Django Template Language (DTL) engine does.
So, all you have to do to switch your template renderer is to change the file extension of the template. Make sure your templates use the right engine’s syntax corresponding to their file extensions!
As an example, these class-based views work for both Jinja2 and DTL in a project set up like in Quick Start:
"""
app layout:
myapp/
├── __init__.py
├── apps.py
├── templates
│ ├── bar.html
│ └── foo.jinja
└── views.py <--(you are here)
"""
from django.views.generic import TemplateView
class FooView(TemplateView):
template_name = 'foo.jinja' # renders with Jinja2
class BarView(TemplateView):
template_name = 'bar.html' # renders with DTL
Note
|
Jinja2 and DTL templates can’t call each other with If you use template inheritance in your project to keep every page looking the same,
you may end up needing to maintain two versions of your commonly used templates, like
|
For advice on converting from a DTL to a Jinja2 template, see Differences with Django Template Engine.
Advanced template pattern matching
If the above default behavior is not to your liking, you can tune it using these OPTIONS
:
"OPTIONS": {
# django-jinja defaults
"match_extension": ".jinja",
"match_regex": None,
"app_dirname": "templates",
}
-
To match only file paths that end with a certain string, use
match_extension
. -
To use regular expressions to match or exclude certain paths, use
match_regex
. -
If both are set, both tests must pass for the backend to try and render the file.
-
If both are disabled with
None
, the backend will try and render any file it finds (and preclude any subsequent engines inTEMPLATES
).
This example matches .html
files instead of .jinja
across the entire project,
but uses a regular expression to exclude matching DTL templates used by the admin interface.
"OPTIONS": {
# Match the template names ending in .html but not the ones in the admin folder.
"match_extension": ".html",
"match_regex": r"^(?!admin/).*",
}
As said previously, when using APP_DIRS
, django-jinja’s backend uses the same
templates
directory as the django template engine. To change it to use another
directory in your apps, you can use the app_dirname
option:
"OPTIONS": {
# Match the templates at <app>/jinja2/*.html`, leaving <app>/templates/ for DTL.
"match_extension": ".html",
"app_dirname": "jinja2",
}
Context processors support
It is a helper to use django’s context processors with jinja2 backend for django 1.8.
"OPTIONS": {
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
],
}
As with the django template engine, this is a default list of context processors, and you can skip setting them if you do not have your own. Furthermore, the purpose of django-jinja’s context processor support is to help with migrations, but context processors are no longer the recommended way to set global variables and functions. For the recommended way, see the next section.
Note
|
Remember that django (1.8.x and 1.9.x) is backward compatible with
the old template api and this has its own trade-offs. If you find yourself using functions
like |
Custom filters, globals, constants and tests
This is the recommended way to set up additional jinja variables, tests, and filters, in your settings.
"OPTIONS": {
"tests": {
"mytest": "path.to.tests.mytestfn",
},
"filters": {
"myfilter": "path.to.filters.myfilterfn",
},
"constants": {
"hello": "hello world",
},
"globals": {
"somefn": "path.to.functions.somefn",
}
}
Set policies
To set environment policies introduced in Jinja2 2.9:
"OPTIONS": {
"policies": {
"ext.i18n.trimmed": True,
},
}
Add additional extensions
django-jinja, by default sets up a great amount of extensions to make your experience
using jinja in django painless. But if you want to add more extensions, you can do it
using the extensions
entry of the backend options:
from django_jinja.builtins import DEFAULT_EXTENSIONS
"OPTIONS": {
"extensions": DEFAULT_EXTENSIONS + [
# Your extensions here...
"path.to.your.Extension"
]
}
Setting the template engine name
Keep in mind that the automatically inferred NAME
for any template backend
will depend on the backend’s import path.
This name is used when you need to specify a template engine by name
(e.g. render_to_string("myapp/template.jinja", context, using="jinja2")
).
In previous versions of django-jinja, the backend’s import path was django_jinja.backend.Jinja2
,
providing the unhelpful default engine name, "backend"
.
Now, the import path django_jinja.jinja2.Jinja2
can be used,
which provides a more meaningful engine name, "jinja2"
.
The old import path is still available for compatability with existing Django projects.
Gettext Style
Jinja2 implements two styles of gettext. You can read about it here: https://jinja.palletsprojects.com/en/latest/extensions/#new-style-gettext
You can switch to concrete style using the newstyle_gettext
entry on
backend options:
"OPTIONS": {
"newstyle_gettext": True,
}
Complete example
This is a complete configuration example with django-jinja’s defaults:
TEMPLATES = [
{
"BACKEND": "django_jinja.jinja2.Jinja2",
"APP_DIRS": True,
"OPTIONS": {
"match_extension": ".jinja",
"match_regex": None,
"app_dirname": "templates",
# Can be set to "jinja2.Undefined" or any other subclass.
"undefined": None,
"newstyle_gettext": True,
"tests": {
# "mytest": "path.to.my.test",
},
"filters": {
# "myfilter": "path.to.my.filter",
},
"globals": {
# "myglobal": "path.to.my.globalfunc",
},
"constants": {
# "foo": "bar",
},
"policies": {
# "ext.i18n.trimmed": True,
},
"extensions": [
"jinja2.ext.do",
"jinja2.ext.loopcontrols",
"jinja2.ext.i18n",
"django_jinja.builtins.extensions.CsrfExtension",
"django_jinja.builtins.extensions.CacheExtension",
"django_jinja.builtins.extensions.DebugExtension",
"django_jinja.builtins.extensions.TimezoneExtension",
"django_jinja.builtins.extensions.UrlsExtension",
"django_jinja.builtins.extensions.StaticFilesExtension",
"django_jinja.builtins.extensions.DjangoFiltersExtension",
],
"bytecode_cache": {
"name": "default",
"backend": "django_jinja.cache.BytecodeCache",
"enabled": False,
},
"autoescape": True,
"auto_reload": settings.DEBUG,
"translation_engine": "django.utils.translation",
}
},
]
Differences with Django Template Engine
Url reversing
django-jinja comes with helpers for reverse urls. Instead of using django’s approach, it uses
a simple function called url
.
{{ url('ns:name', pk=obj.pk) }}
This approach is very flexible, because we do not need additional options to set a result if executing url in one variable. With jinja2 you can use the set template tag for it:
{% set myurl=url("ns:name", pk=obj.pk) %}
Static files
Like urls, static files can be resolved with the simple static
function available globally
in jinja context:
{{ static("js/lib/foo.js") }}
i18n support
django-jinja inherits the jinja2 approach for handling translation strings. You can read more about it here: https://jinja.palletsprojects.com/en/latest/templates/#i18n
{{ _('Hello %(name)s', name=user.name) }}
{% trans name=user.name %}
Hello {{ name }}
{% endtrans %}
Additionally, django-jinja extends django’s makemessages
command to make it work
with jinja2 i18n tags.
If you want more django-like i18n-related tags, you can use extensions from https://github.com/MoritzS/jinja2-django-tags.
Replace jinja filters with django versions
Django and Jinja overlap in a little subset of template filters. To properly handle this, django-jinja uses the jinja versions by default. But if you want a django version of them, you should use the "django_jinja.builtins.extensions.DjangoExtraFiltersExtension" extension.
The affected filters are: title, upper, lower, urlencode, urlize, wordcount, wordwrap, center join, length, random, default, filesizeformat, pprint.
Registering filters in a "django" way.
django-jinja comes with facilities for loading template filters, globals and tests from django applications.
Here an example:
# <someapp>/templatetags/<anyfile>.py
# don't forget to create __init__.py in templatetags dir
from django_jinja import library
import jinja2
@library.test(name="one")
def is_one(n):
"""
Usage: {% if m is one %}Foo{% endif %}
"""
return n == 1
@library.filter
def mylower(name):
"""
Usage: {{ 'Hello'|mylower() }}
"""
return name.lower()
@library.filter
@jinja2.pass_context
def replace(context, value, x, y):
"""
Filter with template context. Usage: {{ 'Hello'|replace('H','M') }}
"""
return value.replace(x, y)
@library.global_function
def myecho(data):
"""
Usage: {{ myecho('foo') }}
"""
return data
@library.global_function
@library.render_with("test-render-with.jinja")
def myrenderwith(*args, **kwargs):
"""
Render result with jinja template. Usage: {{ myrenderwith() }}
"""
return {"name": "Foo"}
from .myextensions import MyExtension
library.extension(MyExtension)
This only works within a Django app. If you don’t have an app for your project, create an app specifically for this purpose and put your templatetags there.
Render 4xx/500 pages with jinja
django-jinja also provides a set of views for easy render 4xx/500 pages using jinja engine:
# yourproject/urls.py
from django_jinja import views
handler400 = views.BadRequest.as_view()
handler403 = views.PermissionDenied.as_view()
handler404 = views.PageNotFound.as_view()
handler500 = views.ServerError.as_view()
Known Issues
-
The above handler500 is broken as of django 2.2. see: https:github.com/niwinz/django-jinja/issues/234
Builtin contrib modules
django-jinja comes with some additional contrib modules that adapt a limited set of external django apps for easy use from jinja templates. Please note that in order to use any of these contrib modules, you’ll need to install the relevant dependent packages yourself first.
Note
|
In django, creating new tags is simpler than in Jinja2. You should remember that in jinja tags are really extensions and have a different purpose than the django template tags. Thus for many things that the django template system uses tags, django-jinja will provide functions with the same functionality. |
easy-thumbnails
Easy Thumbnails is a thumbnail generation library for Django.
INSTALLED_APPS += ('django_jinja.contrib._easy_thumbnails',)
{{ thumbnail(file, size=(400, 400)) }}
{{ user.avatar|thumbnail_url("alias") }}
django-subdomains
Subdomain helpers for the Django framework, including subdomain-based URL routing.
INSTALLED_APPS += ('django_jinja.contrib._subdomains',)
{{ url('homepage', subdomain='wildcard') }}
humanize
Django comes with the humanize library that exposes some useful template filters.
INSTALLED_APPS += ('django_jinja.contrib._humanize',)
License
Copyright (c) 2011-2017 Andre Antukh <niwi@niwi.be>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.