Introduction

django-jinja is a BSD Licensed, simple and nonobstructive 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 opion 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)

  • Compatible with python2 and python3 using same codebase.

  • jinja2 bytecode cache adapted for using django’s cache subsystem.

  • Support for django context processors.

Note

The usage of context processors is not the recommended way anymore, and with django-jinja you can done it setting global data or global constants. See below, in the django 1.8 configuration related section.

Requirements

  • Python 2.7, 3.4 and 3.5

  • Django >= 1.8

  • jinja2 >= 2.7.0

If you are using django < 1.8, you should use django-jinja 1.x versions.

Installation

The simplest way to install django-jinja is using pip:

pip install django-jinja

Quick Start

Add it to django installed apps list:

INSTALLED_APPS += ('django_jinja',)

Followed by the basic template engine configuration:

TEMPLATES = [
    {
        "BACKEND": "django_jinja.backend.Jinja2",
        "APP_DIRS": True,
        "OPTIONS": {
            "match_extension": ".jinja",
        }
    },
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True
    },
]
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.

User Guide

Regex based template matching

By default, django-jinja uses the extension as method to match templates, but if it is not enough, you can extend it using regular experessions:

"OPTIONS": {
    "match_regex": r"^(?!admin/).*", # this is additive to match_extension
}

To disable the extension matching, just set "match_extension" to None:

"OPTIONS": {
    "match_extension": None,
    "match_regex": r"^(?!admin/).*",
}

Context processors support

It is a helper to use django’s context processors with jinja2 backend for django 1.8.

Example setup a bunch of context processors:
"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 usual, this is a default list of context processors and you can skip setting them if you do not have your own. Furthermore, it is now not the recommended way to setup variables in the context and the purpose of its existence is a help for migrations.

Note

Remeber that django (1.8.x and 1.9.x) is backward compatibile with the old template api and this has its own tradeoffs. If you find yourself using functions like render_to_string or render_to_response from django, do not forget to pass the request parameter in order to make context processors work.

Custom filters, globals, constants and tests

This is a way to setup statically (in your settings) additional stuff for jinja:

"OPTIONS": {
    "tests": {
        "mytest": "path.to.tests.mytestfn",
    },
    "filters": {
        "myfilter": "path.to.filters.myfilterfn",
    },
    "constants": {
        "hello": "hello world",
    },
    "globals": {
        "somefn": "path.to.functions.somefn",
    }
}

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 extesions, 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"
    ]
}

Gettext Style

Jinja2 implements two styles of gettext. You can read about it here: http://jinja.pocoo.org/docs/dev/extensions/#newstyle-gettext.

You can switch to concrete style using the newstyle_gettext entry on backend options:

"OPTIONS": {
    "newstyle_gettext": True,
}

Overwrite the default app templates directory

As we said previously, django-jinja backend for django 1.8, uses the same directory for templates as the django template engine. But in some circumstances you may want to change it to use another directory. You can overwrite the default value with the app_dirname option:

"OPTIONS": {
    "app_dirname": "jinja2",
}

Complete example

This is a complete configuration example with django-jinja’s defaults:

TEMPLATES = [
    {
        "BACKEND": "django_jinja.backend.Jinja2",
        "APP_DIRS": True,
        "OPTIONS": {
            # Match the template names ending in .html but not the ones in the admin folder.
            "match_extension": ".html",
            "match_regex": r"^(?!admin/).*",
            "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",
            },
            "extensions": [
                "jinja2.ext.do",
                "jinja2.ext.loopcontrols",
                "jinja2.ext.with_",
                "jinja2.ext.i18n",
                "jinja2.ext.autoescape",
                "django_jinja.builtins.extensions.CsrfExtension",
                "django_jinja.builtins.extensions.CacheExtension",
                "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.

Reverse urls in templates
{{ 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:

Example resolving static files
{{ static("js/lib/foo.js") }}

i18n support

django-jinja inherits the jinja2 approach for handling translation strings. You can read more about it here: http://jinja.pocoo.org/docs/dev/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.

Registring 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.contextfilter
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)

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

  • Previously to django 1.8, some way of using i18n related functions are not properly parsed with makemessages.

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.

django-pipeline

Pipeline is an asset packaging library for Django (official description).

Warning

This plugin is deprecated, django-pipeline comes with good jinja support and it should be used.

You can use the native django-pipeline suport for jinja using the "pipeline.jinja2.ext.PipelineExtension" extension.

Activate plugin (settings.py)
INSTALLED_APPS += ('django_jinja.contrib._pipeline',)
Usage
{{ compressed_css("alias") }}
{{ compressed_js("alias") }}

easy-thumbnails

Easy Thumbnails is a thumbnail generation library for Django.

Activate plugin (settings.py)
INSTALLED_APPS += ('django_jinja.contrib._easy_thumbnails',)
Usage
{{ thumbnail(file, size=(400, 400)) }}
{{ user.avatar|thumbnail_url("alias") }}

django-subdomains

Subdomain helpers for the Django framework, including subdomain-based URL routing.

Activate plugin (settings.py)
INSTALLED_APPS += ('django_jinja.contrib._subdomains',)
Usage
{{ url('homepage', subdomain='wildcard') }}

humanize

Django comes with the humanize library that exposes some useful template filters.

Activate plugin (settings.py)
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.