【Flask】Jinja2の制御構文(if, for in)でクライアントサイドを柔軟に簡素化する

前提条件

CHECK.1
ディレクトリ
ディレクトリ
flask/
 ┣ app.py
 ︙
 ┗ templates/
   ┗ index.html

ターミナル(コマンドプロンプト)のカレントディレクトリは、 flaskフォルダにあるものとします。

CHECK.2
app.py
app.py
# -*- coding: utf-8 -*-
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
	return render_template('index.html')

if __name__ == '__main__':
	app.run()
CHECK.3
index.html
index.html
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Hello Jinja2</title>
  </head>
  <body>
    <h1>Hello Jinja2</h1>
    <p>Jinja2でHTMLファイルを読み込みことに成功しました。</p>
  </body>
</html>
CHECK.4
動作確認
ターミナル
$ python app.py

http://127.0.0.1:5000/

END

条件分岐(if)

Jinja2で条件分岐する最小のプログラムは以下のようになります。

{% if 条件式1  %}
	<!-- 条件式1がtrueの場合の処理 -->
{% elif 条件式2 %}
	<!-- 条件式2がtrueの場合の処理 -->	
{% else %}
	<!-- 条件式1、2のいずれも満たさない場合の処理 -->
{% endif %}

HTMLと組み合わせて使う

index.html
<!-- 中略 -->
<body>
	<h1>Jinja2で条件分岐</h1>
	{% set val = 1 %}
	<p>val:{{ val|e }}</p>
	
	<!-- valに値が入っていれば… -->
	{% if val %}
		{% if val==0 %}
		<p>変数valには0が格納されています。</p>	
		{% elif val==1 %}
		<p>変数valには1が格納されています。</p>
		{% else %}
		<p>変数valには0と1以外の値が格納されています。</p>
		{% endif %}
	{% endif %}
</body>

ループ(for in)

index.html
<!-- 中略 -->
<body>
	<h1>forループ</h1>
	{% for i in range(5) %}
	<p>{{ i|e }} こんにちは</p>
	{% endfor %}
</body>

1次元配列

Jinja2で1次元配列の要素を順番に取り出す最小のプログラムは以下のようになります。

{% for i in 1次元配列 %}
	<!-- ループの中身 -->
{% endfor %}

HTMLと組み合わせて使う場合

index.html
<!-- 中略 -->
<body>
	<h1>1次元配列のループ</h1>
	{% set array = ['a', 'b', 'c', 'd', 'e'] %}
	<p>配列:{{ array|e }}</p>    
	<ul>
		{% for i in array %}
		<li>{{ i|e }}</li>
		{% endfor %}
	</ul>
</body>

インデックスも欲しい場合

Python
items = ['a', 'b', 'c', 'd', 'e']
for index, item in enumerate(items):
	print(index, item)

Pythonのenumerate()みたいなことをJinja2でしたい場合は、ループ中で使用することができるloop.indexまたはloop.index0変数を使用します。

  • loop.index1から始まるインデックス
  • loop.index00から始まるインデックス
index.html
<!-- 中略 -->
<body>
	<h1>1次元配列のループ</h1>
	{% set array = ['a', 'b', 'c', 'd', 'e'] %}
	<p>配列:{{ array|e }}</p>    
	<ul>
		{% for i in array %}
		<li>{{ loop.index }}, {{ i|e }}</li>
		{% endfor %}
	</ul>
</body>

2次元配列

Jinja2で2次元配列の要素を順番に取り出す最小のプログラムは以下のようになります。

{% for i in 2次元配列 %}
	{% for j in i %}
		<!-- ループの中身 -->
	{% endfor %}
{% endfor %}

HTMLと組み合わせて使う場合

index.html
<!-- 中略 -->
<body>
	<h1>2次元配列のループ</h1>
	{% set array = [['a', 'b', 'c', 'd', 'e'], ['A', 'B', 'C', 'D', 'E']] %}
	<p>配列:{{ array|e }}</p>    
	<ul>
		{% for i in array %}
		<ul>
			{% for j in i %}
			<li>{{ j|e }}</li>
			{% endfor %}
		</ul>
		{% endfor %}
	</ul>
</body>

インデックスも欲しい場合

1次元配列のときと同様に、loop.indexまたはloop.index0変数を使用します。

ただし、1つ目のループのインデックスは、2つ目のループのインデックスで上書きされてしまうので、別途変数にセットする必要があるので注意しましょう。

index.html
<!-- 中略 -->
<body>
	<h1>2次元配列のループ</h1>
	{% set array = [['a', 'b', 'c', 'd', 'e'], ['A', 'B', 'C', 'D', 'E']] %}
	<p>配列:{{ array|e }}</p>    
	<ul>
		{% for i in array %}
		<p>{{ loop.index }}番目のリスト</p>
		<ul>
			{% set index = loop.index0 %}<!-- ここで1つ目のループのインデックスをセット -->
			{% for j in i %}
			<li>array[{{ index }}][{{ loop.index0 }}] : {{ j|e }}</li>
			{% endfor %}
		</ul>
		{% endfor %}
	</ul>
</body>

辞書型配列

Jinja2で辞書型配列の要素を順番に取り出す最小のプログラムは以下のようになります。

{% for key, value in 辞書型配列.items() %}
	<!-- ループの中身 -->
{% endfor %}

HTMLと組み合わせて使う場合

index.html
<!-- 中略 -->
<body>
	<h1>辞書型配列のループ</h1>
	{% set dict = {'みかん':100, 'りんご':200, 'ぶどう':400, 'なし':300, 'メロン':800} %}
	<p>配列:{{ dict|e }}</p>
	<dl>	
		{% for key, value in dict.items() %}
		<dt>{{ key|e }}</dt>
		<dd>{{ value|e }}</dd>
		{% endfor %}
	</dl>
</body>

インデックスも欲しい場合

1次元配列、2次元配列のときと同様に、loop.indexまたはloop.index0変数を使用します。

あえて説明する箇所も無いので割愛します。

loop.* 変数を使う

最後にJinja2のループの中でのみ使用できる変数を紹介します。

よく使うloop.* 変数の一覧
オプション 説明
loop.index 1から始まるインデックス
loop.index0 0から始まるインデックス
loop.revindex 後ろから数えるインデックス(5要素ある場合、5, 4, 3, 2, 1)
loop.revindex0 後ろから数えるインデックス(5要素ある場合、4, 3, 2, 1, 0)
loop.first ループの1番目の要素だった場合、Trueを返す(それ以外はFalse)
loop.last ループの最後の要素だった場合、Trueを返す(それ以外はFalse)
loop.previtem ループの1つ前の要素を返す(無い場合は空白)
loop.nextitem ループの1つ次の要素を返す(無い場合は空白)
参考 List of Control Structuresjinja.palletsprojects.com
index.html
<!-- 中略 -->
<body>
	<h1>loop.* 変数を使う</h1>
	{% set array = ['a', 'b', 'c', 'd', 'e'] %}
	<p>配列:{{ array|e }}</p>
	<table>
		<thead>
			<tr><th>i</th><th>loop.index</th><th>loop.index0</th><th>loop.revindex</th><th>loop.revindex0</th><th>loop.first</th><th>loop.last</th><th>loop.previtem</th><th>loop.nextitem</th></tr>
		</thead>
		<tbody>
		{% for value in array %}	
			<tr><td>{{ i|e }}</td><td>{{ loop.index|e }}</td><td>{{ loop.index0|e }}</td><td>{{ loop.revindex|e }}</td><td>{{ loop.revindex0|e }}</td><td>{{ loop.first|e }}</td><td>{{ loop.last|e }}</td><td>{{ loop.previtem|e }}</td><td>{{ loop.nextitem|e }}</td></tr>
		{% endfor %}	
		</tbody>
	</table>		
</body>

連載目次:FlaskでWebアプリケーションを開発するためのロードマップ

入門編:10記事

入門編の10記事を順に読んでいけば、FlaskでWebアプリケーションを開発する必要最小限のことが学べます。

簡単なアプリケーションであれば、セキュリティ上の観点を考慮しなかった場合公開できるでしょう。

実践編

実践編の記事では、FlaskでWebアプリケーションを公開するために欠かせないセキュリティのことや実践的なテクニックを紹介しています。

ここまで読み込めば、あとはアイディア次第でいろんなWebアプリケーションを公開できるでしょう!

  1. セッション
  2. Cookie
  3. メソッドベース・ディスパッチ
  4. ベーシック認証・Digest認証
  5. ログイン機能
  6. サーバーサイドからクライアントサイドにpandas.Series、pandas.DataFrameを渡す
  7. クライアントサイド(form)からPOSTされた1、2次元配列を受け取る
  8. matplotlibを使う
  9. Bootstrap4を使う
  10. フォームの非同期通信(Ajax, jQuery)