【Flask×Jinja2】クライアントサイドで変数を処理(アサイン、フィルター、エスケープ)する

前提条件

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

変数のアサイン

Jinja2では、{% set %}ブロックを使用することで、クライアントサイドで変数を定義したり、サーバーサイドから受け取った変数を再定義することが出来ます。

{% set 変数 = 値 %}
app.py
# 中略
@app.route('/')
def index():
	val1 = 'abcdefg'
	return render_template('index.html', val1=val1)
index.html
<!-- 中略 -->
<body>
	<h1>変数のアサイン</h1>

	<!-- 1.サーバーサイドから受け取った変数を表示 -->
	<p>val1:{{ val1 }}</p>

	<!-- 2.サーバーサイドから受け取った変数を再定義して表示 -->
	{% set val1 = 'ABCDEFG' %}
	<p>val1:{{ val1 }}</p>

	<!-- 3.クライアントサイドで変数を定義して表示 -->
	{% set val2 = 'あいうえお' %}
	<p>val2:{{ val2 }}</p>
</body>

フィルター

Jinja2テンプレートで何かしらの変数を扱うとき、その変数の値を変えずに、ある処理を施して変数の値を表示したい、という場合があると思います。

そんなときはJinja2の『フィルター機能』を使用します。

{% 変数|フィルター名 %}

具体例を見てみましょう。

次の例では、変数str"i am ken."という文字列をセットし、『原文→全部大文字→原文』という順番で、変数strの値を変えずに表示させています。

index.html
<!-- 中略 -->
<body>
	<h1>フィルター</h1>
	{% set str = 'i am ken.' %}
	<p>str:{{ str }}</p>
	<p>str|upper:{{ str|upper }}</p>
	<p>str:{{ str }}</p>
</body>

フィルターには大きく分けて2種類存在するので、順に紹介します。

ビルトインフィルター

Jinja2に予め用意されているフィルターのことをビルトインフィルターと呼びます。

参考 List of Builtin Filtersjinja.palletsprojects.com

値(数値、文字列)に適用するビルトインフィルター

書式 説明
値|upper 値をすべて大文字にする
値|lower 値をすべて小文字にする
値|capitalize 値の最初の文字を大文字に、他を小文字にする
値|abs 絶対値を得る
値|length 文字数を数える(空白文字もカウント)
値|wordcount 単語数を数える
値|default(初期値) 値が未定義だった場合の初期値を設定する
値|escapeまたは、値|e 値をエスケープ処理する
値|safe 値をエスケープ処理しない(HTMLやスクリプトがタグとしてブラウザ認識される)
値|format Pythonのフォーマットを適用する
値|trim 先頭および末尾の空白を削除する。
値|urlencode URLエンコードする
値|replace(検索値, 置換値) 値を置換する(検索値に正規表現は使えません)

1次元配列に適用するビルトインフィルター

書式 説明
1次元配列|first リストの最初の値を取り出す
1次元配列|last リストの最後の値を取り出す
1次元配列|max リストの最大値を返す
1次元配列|min リストの最小値を返す
1次元配列|length リストの要素数を返す
1次元配列|random リストからランダムに要素を1つ得る
1次元配列|unique 重複を取り除いた一意の要素のリストを得る
1次元配列|reverse リストを逆順にする
1次元配列|sort リストをソートする

テンプレートフィルター

後で書きます

エスケープ

XSSなどの脆弱性対策のためにJinja2では、デフォルトでエスケープが有効化されています。

このエスケープ処理は、{% autoescape boolean %}ブロックで有効化/無効化を切り替えることができます。

index.html
<!-- 中略 -->
<body>
	{% autoescape true %}
	<!-- エスケープ有効化エリア(デフォルト) -->
	{% endautoescape %}

	{% autoescape false %}
	<!-- エスケープ無効化エリア(危険!) -->
	{% endautoescape %}
</body>

では、どうして危険を犯してまでもエスケープを無効化させたいのでしょうか。

答えは、サーバーサイドから文字列として渡した値をHTMLタグやスクリプトとして、動的に処理したいときがあるからです。

変数単位でエスケープを無効化する

ビルトインフィルターのsafeフィルターを使用します。

index.html
<!-- 中略 -->
<body>
	<h1>エスケープ</h1>
	{% set str = '<p>あいうえお</p>' %}
	{{ str }}<!-- HTMLタグとして認識されない -->
	{{ str|safe }}<!-- HTMLタグとして認識される -->
</body>

変数単位でエスケープを有効化する

ビルトインフィルターのescapeフィルターを使用します。省略してeでも可。

index.html
<!-- 中略 -->
<body>
	<h1>エスケープ</h1>
	{% set str = '<p>あいうえお</p>' %}
	{% autoescape false %}<!-- ここからエスケープ処理を無効化 -->
	{{ str }}<!-- HTMLタグとして認識される -->
	{{ str|escape }}<!-- HTMLタグとして認識されない -->
	{{ str|e }}<!-- HTMLタグとして認識されない -->
	{% endautoescape %}
</body>

ソースの空白行を削除する

Jinja2の{% hoge %}ブロックを使うと、ソースコードに空白の行が残ってしまいます。動作的には問題ありませんが、良い気はしませんね。

{% set %}ブロックを例に見てみましょう。

index.html
<!-- 中略 -->
<body>
	<h1>変数のアサイン</h1>
	{% set val = 'aiueo' %}
	<p>val:{{ val }}</p>
</body>

この空白行は、ブロックの{%部分に-を付けて、{%- hoge %}のようにすれば、削除することが出来ます。

index.html
<!-- 中略 -->
<body>
	<h1>変数のアサイン</h1>
	{%- set val = 'aiueo' %}
	<p>val:{{ val }}</p>
</body>

このとき、%-の間に空白行を空けてはいけません。

index.html
<!-- 中略 -->
<body>
	<h1>変数のアサイン</h1>
	{% - set val = 'aiueo' %}
	<p>val:{{ val }}</p>
</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)