django-jinja - Jinja2 Template Engine for Django

1. Introduction

django-jinja is a BSD Licensed, simple and nonobstructive jinja2 integration with Django.
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 …​
There are other projects that attempt do same thing: Djinja, Coffin, etc…​ Why one more?
  • Unlike Djinja, django-jinja is not intended to replace a Django template engine, but rather, it complements the Django’s template engine, giving the possibility to use both.
  • Unlike Coffin, the django-jinja codebase is much smaller and more modern. This way is much more maintainable and easily understandable how the library works.

1.1. Features

  • Auto-load templatetags compatible with Jinja2 on same way as Django.
  • 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 use django cache subsystem.
  • Django 1.8 support out of the box.

2. Requirements

  • Python 2.7, 3.3 or 3.4
  • Django 1.5, 1.6, 1.7 and 1.8
  • jinja2 >= 2.7.0

2.1. Installation

The simplest way to install django-jinja is using pip:
pip install django-jinja
Add add it to django installed apps list:
INSTALLED_APPS += ('django_jinja',)

3. User guide (django <= 1.7)

3.1. Introduction

Since django-jinja supports two ways to setup (mainly for django ⇐ 1.7 and django >= 1.8) this section tries expain how to setup it for django ⇐ 1.7.

3.2. Quick Setup

The first step for configure django-jinja is replace default django template loaders:
TEMPLATE_LOADERS = (
    'django_jinja.loaders.FileSystemLoader',
    'django_jinja.loaders.AppLoader',
)
django-jinja template loaders inherit’s from a django template loaders and works like middleware, intercepts by predefined condition templates for render with jinja2 engine.
The simplest condition is using file extensions:
DEFAULT_JINJA2_TEMPLATE_EXTENSION = '.jinja'
With this settings, django-jinja intercepts all templates with .jinja extension and render them with jinja2 engine and templates that not matches the .jinja extension are forwarded to django templates engine.

3.3. Advances settings

3.3.1. Regex based template matching

Additionaly, django-jinja exposes, more advanced matcher, using regular exceptions:
# Same behavior of default intercept method
# by extension but using regex (not recommended)
DEFAULT_JINJA2_TEMPLATE_INTERCEPT_RE = r'.*jinja$'

# More advanced method. Intercept all templates
# except from django admin.
DEFAULT_JINJA2_TEMPLATE_INTERCEPT_RE = r"^(?!admin/).*"
Furthermore, the regex matching also can be combined with the extension based matching, allowing match by extension and regex in the same time.

3.3.2. Setup autoescape

You can enable or disable autoescaping with JINJA2_AUTOESCAPE setting.
# Enable/Disable autoescaping (default: True)
JINJA2_AUTOESCAPE = True

3.3.3. Mute url resolve exceptions

Sometimes, you want simply mute exceptions related to url resolving. You can mute exceptions with the following entry on your settings:
# Mute reverse url exceptions (default: False)
JINJA2_MUTE_URLRESOLVE_EXCEPTIONS = True

3.3.4. Set custom loader

django-jinja by default setups a proper template loader for load templates from django’s TEMPLATES_DIRS and app specific templates. But in some circumstances you want override completely it. This can be done using the following entry on your settings:
# Set custom loader
import jinja2
JINJA2_LOADER = jinja2.FileSystemLoader(["/some/path"])

3.3.5. Add additional extensions

django-jinja, by default set up a great amount of extensions for make your experience using jinja in django painless. But if you want add more extesions, you can done using JINJA2_EXTENSIONS:
from django_jinja.builtins import DEFAULT_EXTENSIONS

JINJA2_EXTENSIONS = DEFAULT_EXTENSIONS + [
    # Your extensions here...
    "path.to.your.Extension"
]

3.3.6. Template bytecode cache

django-jinja supports the Jinja2’s template bytecode caching system. Including an implementation for makes use of Django’s built-in cache framework.
# Enable bytecode cache (default: False)
JINJA2_BYTECODE_CACHE_ENABLE = False

# Cache backend name for bytecode cache (default: "default")
JINJA2_BYTECODE_CACHE_NAME = "default"

# Specify custom bytecode cache subclass (default: None)
JINJA2_BYTECODE_CACHE_BACKEND = "path.to.you.cache.class"

4. User guide (for django >= 1.8)

4.1. Introduction

Since django 1.8, multiple template engine support was added to django, and django-jinja comes with support for it. Implementing the backend interface and new configuration syntax.
Django also comes with basic jinja backend, but it not has a lot of facilities and integrations with the rest of django. django-jinja comes to the rescue and add everything missing.
django-jinja comes with that brief list of differences with django’s built-in backend.
  • find the templates as usual in "<appname>/templates" directory instead of "<appname>/jinja2" directory (you can overwrite this behavior, see below).
  • preload template tags.
  • gettext message loading (i18n).
  • 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.
Warning
The settings variables used for configure django-jinja for django ⇐ 1.7 does not works if you are using the django 1.8 backend.

4.2. Quick Setup

This is a quick example of how to configure django-jinja with django 1.8 configuration formata:
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 app templates directory for django-jinja backend, take cara about the order template engines, because the django-jinja backend by default uses the same directory for the templates that the django template engine. If you put the django engine first every jinja template will be found by the django engine.

4.3. Advanced options

4.3.1. Regex based template matching

Additionaly, django-jinja exposes, more advanced matcher, using regular exceptions:
"OPTIONS": {
    "match_regex": r"^(?!admin/).*", # this is additive to match_extension
}
Furthermore, the regex matching also can be combined with the extension based matching, allowing match by extension and regex in the same time.

4.3.2. Context processors support

This a helper for make possible use django’s contect 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 forget setup them if you do not have own. Furthermore, it is now not recommended way to setup variables in the context and the purpose of its existence is a help for migrations.

4.3.3. Custom filters, globals, constants and tests

django-jinja backend offers a helper options for easy setup constants, global functions, tests and filters:
"OPTIONS": {
    "tests": {
        "mytest": "path.to.tests.mytestfn",
    },
    "filters": {
        "myfilter": "path.to.filters.myfilterfn",
    },
    "constants": {
        "hello": "hello world",
    },
    "globals": {
        "somefn": "path.to.functions.somefn",
    },

4.3.4. Add additional extensions

django-jinja, by default set up a great amount of extensions for make your experience using jinja in django painless. But if you want add more extesions, you can done using extensions entry on backend options:
from django_jinja.builtins import DEFAULT_EXTENSIONS

"OPTIONS": {
    "extensions": DEFAULT_EXTENSIONS + [
        # Your extensions here...
        "path.to.your.Extension"
    ]
}

4.3.5. 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,
}

4.3.6. 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 for use other directory. You can overwrite the default value with app_dirname option:
"OPTIONS": {
    "app_dirname": "jinja2",
}

4.3.7. 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",
            ],
            "autoescape": True,
            "auto_reload": settings.DEBUG,
            "translation_engine": "django.utils.translation",
        }
    },
]

5. Differences

5.1. Url reversing

django-jinja comes with helpers for reverse urls. Instead of using the 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 for 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) %}

5.2. Static files

Like urls, the static files can be resolved with simple static function available globally in jinja context:
Example resolving static files
{{ static("js/lib/foo.js") }}

5.3. i18n support

django-jinja inherits the jinja2 approach for handle 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 the django’s makemessages command for 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.

5.4. Replace jinja filters with django versions

Django and Jinja overlaps in a little subset of template filters. For handle proper this, django-jinja gets the decistion to use the jinja versions. 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.

5.5. 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)

5.6. 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()

6. Known Issues

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

7. Builtin contrib modules

django-jinja comes with some additional contrib modules that adapts limited set of external django apps for use it easy from jinja templates. Please note that in order to use any of these contrib modules, you’ll need to install the relevant dependent package 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.

7.1. django-pipeline

Pipeline is an asset packaging library for Django (official description).
Warning
This plugin is deprecated, django-pipeline is come 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") }}

7.2. 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") }}

7.3. 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') }}

7.4. humanize

Django comes with humanize library that exposes some useful template filters.
Activate plugin (settings.py)
INSTALLED_APPS += ('django_jinja.contrib._humanize',)

No comments:

Post a Comment