Jinja2の使い方を紹介。Flask+Jinja2

2019年4月18日木曜日

Python

t f B! P L

Jinja2の使い方を紹介。Flask+Jinja2

この記事では、FlaskのテンプレートエンジンであるJinja2の使い方について、解説します。Jinja2はPythonのWebサービスでよく用いられるため、覚えておいて損はないです。

もしPythonのフレームワークであるDjangoを経験していれば、構文がよく似ているため、すんなりと理解することが出来ると思います。

enter image description here

Jinja2とは

Jinja2は、Python用のテンプレートエンジンの1つ。Pythonの有名なフレームワークである、Djangoのテンプレートエンジンに構文がよく似ています。Jinja2は基本的にHTMLやXML生成に使われるが、どのような文書でも生成できます。(例えばソースコードやMarkdownなど)

Jinja2は、Flask内のテンプレートエンジンとして組み込まれています。そのため、Flaskの環境があれば、追加のインスール不要でJinja2が使えます。

ちなみにJinja2 は、 「じんじゃ(神社)」 と読むそうです。

はじめに

FlaskでJinja2を使用する場合は、Jinja2のテンプレートファイルを、templatesディレクトリに作成します。templates以外のフォルダにJinja2のテンプレートを作成しても、Flaskは認識してくれませんのでご注意を。

project_root
├── app.py
└── templates
    ├── index.html
    └── about.html

Jinja2の基本的な使い方

ほほ公式のままですが、下記がJinja2のテンプレートのサンプルです。

<title>{{title}}</title>
<ul>
{% for user in users %}
  <li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>

テンプレート構文

Jinja2テンプレートの基本的な構文です。

構文 説明
{% ... %} ステートメント
{{ ... }}
{# ... #} コメント
# ... ## 行ステートメント

Jinja2にパラメータを渡す

render_template()に名前付き引数を指定すると、Jinja2のテンプレートにパラメーターを渡すことができます。パラメータを渡す、いくつかのサンプルコードを紹介します。

文字列(str)を渡す

[Python]

from flask import render_template

@app.route("/index")
def index():
  msg = 'Hello'
  return render_template('index.html', msg = msg)

[Teamplate]

<div>
  <p>{{message}}</p>
</div>

辞書(dict)を渡す

[Python]

from flask import render_template

@app.route("/index")
def index():
  user = { 'name': '山田太郎', 'age': 20 }
  return render_template('index.html', user = user)

[Teamplate]

<div>
  <p>ユーザ名: {{user.name}}</p>
  <p>年齢: {{user.age}}</p>
</div>

クラスを渡す

[Python]

from flask import render_template

@app.route("/index")
def index():
  user = User()
  user.name = '山田太郎'
  user.age = 200
  return render_template('index.html', user = user)

[Teamplate]

<div>
  <p>ユーザ名: {{user.name}}</p>
  <p>年齢: {{user.age}}</p>
</div>

Jinja2に複数のパラメータを渡すには

Jinja2のテンプレートに複数のパラメータを渡すには、Pythonのrender_template関数呼び出し時に、名前付き引数を複数指定してやるだけです。

[Python]

from flask import render_template

@app.route("/index")
def index():
  user = User()
  user.name = '山田太郎'
  user.age = 200
  return render_template('index.html', msg = "Hello", user = user)

[Teamplate]

<p>{{msg}}</p>
<div>
  <p>ユーザ名: {{user.name}}</p>
  <p>年齢: {{user.age}}</p>
</div>

Jinja2のテンプレートで分岐する(if)

if文は、Pythonの記法とほとんど同じです。Pythonとの違いとして、行末のコロン(:)が不要なことと、if文を終了するときは、{% endif %}と書きます。

{% if user.age < 20 %}
  <p>未成年</p>
{% elif user.age >= 20 and user.age <= 60 %}
  <p>現役</p>
{% else %}
  <p>シルバー</p>
{% endif %}

Jinja2のテンプレートで繰り返し(for)

<select>
{% for user in users %}
  <option value="{{user.id}}" {{'selected' if user.id == sel_id else ''}}>{{user.name}}</option>
{% endfor %}
</select>

エスケープ(Jinja2の"{{", "{%"をエスケープ)

Jinja2テンプレートの特殊文字である"{{"や "{%“を、文字として扱いたい場合は、{% raw %}ブロックを使用します。{% raw %}ブロック内に書かれた内容は、すべて文字として扱われるため、”{{"や "{%"などの特殊文字を文字として表示させる事ができます。

{% raw %}
  <ul>
    {% for item in seq %}
      <li>{{ item }}</li>
    {% endfor %}
  </ul>
{% endraw %}

Jinja2 のテンプレートを継承する

ページヘッダーや、サイドメニューなど、どのページでも共通している部分を、共通のレイアウトとして作成しておき、そのレイアウトを継承させた、子テンプレートを作成できます。

継承を行なった子テンプレートは、共通部分のHTMLを記述する必要がなく、個別部分のHTMLだけを記述します。これにより、もし共通部分のレイアウトに手が入る場合、共通のレイアウトを1箇所修正するだけで済むため、保守性がよくなります。

[base.html]

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>{% block title %}{% endblock %} - サンプルサイト</title>
  {% block head %}
  <link rel="stylesheet" href="style.css" />
  {% endblock %}
</head>
<body>
<!-- 共通ヘッダ -->
<header>
   <h1>{% block title %}{% endblock %}</h1>
</header>
<!-- コンテンツ部分 -->
<div id="content">
  {% block body %}
  {% endblock %}
</div>
</body>
</html>

[子テンプレート]

{% extends "base.html" %}
{% block title %}ページタイトル{% endblock %}
 
{% block head %}
    {{ super() }}
    <style type="text/css">
        .message { color: #f00; }
    </style>
{% endblock %}
         
{% block content %}
  <p class="message">
    Welcome to my awesome homepage.
  </p>
{% endblock %}

Jinja2 のインクルード機能

Jinja2のインクルード(include)機能を使うと、継承を使うほどでもないけど、ページで共通している部分をコンポーネント化できます。例えば、ページングを行うページネーションなどをコンポーネント化しておくと、インクルード機能で複数の画面から使用できて便利です。

[pagger.html]


```html
<!--前のページ-->
{% if has_previous %}
<a  href="mypage?page={{ previous_page }}">前</a>
{% endif %}

<!--ページ番号-->
<span>{{ page.number }} / {{ page.paginator.num_pages }}</span>

<!--次のページ-->
{% if has_next %}
<a  href="mypage?page={{ next_page }}">次</a>
{% endif %}

上で作成したテンプレートはincludeで読み込みます。

<nav>
    <!--上で作成した部品を読み込む-->
    {% include "pagger.html" %}
</nav>
スポンサーリンク

QooQ