DjangoでJinja2を使ってみよう
Django と Jinja2
Jinja2 は人気のあるテンプレートエンジンです。Jinja2 は Django Template を意識して開発されているため、テンプレートファイルがよく似ています。
Django Template と比較すると高機能なため使いたくなるときもあるとはずです。
Django 1.8 から Jinja2 を Django のバックエンドにできるようになっています。
インストールと準備
Djnago から Jinja2 を利用するためには、jinja2 をインストールしている必要があります。
code: bash
$ pip install jinja2
プロジェクトのsettings.py で TEMPLATEにJinja2を登録します。
code: python
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'environment': 'プロジェクト名.jinja2.environment'
},
},
{
'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',
],
},
},
]
プロジェクトのsettings.pyと同じ場所にjinja2.pyファイルを作成し、次のコードを追加します。
code: jinja2.py
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
})
return env
これで、Djangoテンプレートタグstatic と url を Jinja2テンプレートで使用できるようになります
code: Django テンプレートでの記述
{% url 'index' variable %}
{% static 'path' %}
code: Jinja2 テンプレートでの記述
{{ static('path')}}
別に記述した関数もsettings.py の environment()関数に登録することで、
Jinja2 テンプレートで使える関数となります。
APP_DIRS: True を設定しているので、 アプリケーションのディレクトリにあるjinja2 ディレクトリがテンプレート検索パスに追加されます。
Django Template とJinja2の両方を柔軟に使い分けることもできるので、Djangoが提供する管理パネルや、Djangoテンプレートを使用する他のプラグインもそのまま使用できます。
コード中でJinja2を利用する
コード中にピンポイントでJinja2の機能を使いたいようなときは、
次のようにしてレンダリングすることができます。
code: python
from django.template import engines
template = django_engine.from_string("Hello {{ name }}!")
Djnago Template と Jinja2 の違い
Jinja2 は Django Template を意識して開発されていますが、若干の違いが存在します。
辞書型オブジェクトへのアクセス方法
辞書型オブジェクトの変数は、Django Template ではドット記法しか記述できませんが、Jinja2では辞書のキーを指定した記述も受け付けます。
未定義のコンテキスト変数
Django Template では、定義されていないコンテキスト変数は何もレンダリングされませんが、Jinja2では未定義のコンテキスト変数は例外を発生します。
また、Django Template では、「未定義のコンテキスト変数はFalseとなる」という暗黙のルールが使われることがあります。Jinja2テンプレートを処理するには、コンテキストで使用されるすべての変数を明示的に設定するか、定義されているかをチェックする必要があります。
code: Djnago Template で未定義変数をチェックする例
{% if urls.profile %}
<a href="{{urls.profile}}">My Profile</a>
{% endif %}
code: Jinja2 で未定義変数をチェックする例
{{ if urls.profile is defined %}
<a href="{{urls.profile}}">My Profile</a>
{{ endif }}
CSRF_TOKEN
Django Template は、フォーム要素に CSTF_TOKEN を含む非表示フィールドを挿入するための便利な{% csrf_token %} が提供されています。
Jinja2では、{{ csrf_input }} を使うか、明示的にcsrf_token に設定されたコンテキスト変数を使用します。
code: Django テンプレートの場合
{% csrf_token %}
code: Jinja2 テンプレートの場合
{{ csrf_input }}
もしくは
code: Jinja2 テンプレートの場合
<input type="hidden" name="csrfmiddlewaretoken" value="{{csrf_token}}">
{{ block.super }} と {{ super() }}
テンプレートファイルの継承を行うときに、ベーステンプレートのブロックを指定するとき
Django Template では {{ block.super }} と記述し、Jinja2 では {{ suer() }} と記述します。
Django Template の場合
code: base.html
{% block bodyscripts %}
<script type="text/javascript" charset="utf-8" src="/assets/base.js"></script>
{% endblock %}
code: index.html
{% extends "base.html" %}
{% block bodyscripts %}
{{ block.super }}
<script type="text/javascript" charset="utf-8" src="/assets/index.js"></script>
{% endblock %}
Jinja2 の場合
code: base.html
{% block bodyscripts %}
<script type="text/javascript" charset="utf-8" src="/assets/base.js"></script>
{% endblock %}
code: index.html
{% extends "base.html" %}
{% block bodyscripts %}
{{ super() }}
<script type="text/javascript" charset="utf-8" src="/assets/index.js"></script>
{% endblock %}
capfirst と capitalize
Jinja2 がDjango Template に影響を受けて開発されていることもあり、両者で同じ機能で異なる名前のフィルターはほとんどありません。しかし、値を大文字にするフィルター は名前が異なることに注意してください。
code: Djanto Template の場合
{{ myvalue | capfirst }}
code: Jinja2 の場合
{{ myvalue | capitalize }}
{% include with %} と {% macro %}
Jinja2 のマクロは非常に強力で、よく使用されるイディオムを関数として再利用可能な形にしたものです。
次のJinja2テンプレートはDjango Fromsのインスタンスを受け取り、Jinja2のマクロを使用してHTMLレイアウトを完全に生成するときの例です。
code: Jinja2 でのフォーム
{% macro input_field(bound_field, hide_labels, extra_label_classes="", extra_control_classes="") -%}
{%- endmacro %}
{% for name, field in form.fields.items() %}
{{ input_field(formname, form.hide_labels) }} {% endfor %}
Django ではマクロを使うことができませんが、{% include with %} を使うことで同様のことができます。
code: Django Template でのフォーム
{% for name, field in form.fields.items %}
{% include "_field.html" with bound_field=form|get_bounded_field:name hide_labels=form.hide_labels %}
{% endfor %}
URLパターンの取得
Django Template と Jinja2 との互換性を考えるときにもっとも出くわすもののひとつが、URLパターンを取得するときです。
code: Django Template
{% url 'profile' 'freddie' %}
code: Jinja2
{{ url('profile', 'freddie') }}
テンプレートファイルの検索パスと優先度
Django は次の順序でテンプレートファイルを検索し、最初に見つかったものを使用します。
Django のプロジェクトのsettings.py で設定したTEMPLATESの設定順
DIRSで指定したディレクトリのリストの設定順
BACKENDの設定順
Django のプロジェクトのsettings.py で設定した INSTALLED_APPS の設定順
例えば、アプリケーションAでテンプレートファイル A/templates/user_form.html があるとします。
新しくアプリケーションBを作成してB/templates/user_form.html のテンプレートファイルを準備としたとき問題なります。 それは、INSTALLED_APPS = [..., 'A', 'B'] となっていれば、アプリケーションBで作ったつもりのuser_form.htmlではなく、アプリケーションAで使用していたuser_form.htmlが使われてしまうからです。
テンプレートファイルの命名には気を配るようにしましょう。
テンプレートファイルはアプリケーション名あるいはアプリケーションを表すSLUG_IDをテンプレートファイルに付与しておくことも回避策のひとつとなるでしょう。