今回はFlaskのテンプレートエンジンを使ってみたいと思います。FlaskではJinja2のテンプレートエンジンを使用しています。Jinja2でのテンプレートとは"{%%}"や"{{}}"などの記法を用いて作成され、それらを活用して、文字列の埋め込み、if分岐などを実現しているものです。FlaskではサーバでのHTMLのレンダリングに使用されています。
Flaskへの実装
まずは前回のindex.htmlおよびapp.pyを変更します。
from flask import Flask, render_template, request # request
app = Flask("__name__")
app.jinja_env.line_statement_prefix = '#' # line statementsの有効化
database = []
@app.route("/")
def root():
data1 = "hoge1"
data2 = {"id": "1", "name": "test", "data": "hoge2"}
data3 = ["hoge3", "huga3"]
return render_template("index.html", data1=data1, data2=data2, data3=data3) # テンプレートのレンダリング(テンプレートの実行)を実施。また引数にapp.pyの変数とhtml側の変数のバインド関係を記載
@app.route("/api/v1/<string:userId>", methods=['GET','POST','PUT','DELETE'])
def api_get(userId):
#..省略..#
if __name__ == "__main__":
app.run(debug=True)
テンプレートの記載方法
記法
テンプレートの記法には主に下記のようなものがあります。また公式HPのFlask templateにも記載があります。
1 | 2 |
---|---|
{% %} | 変数 |
{{ }} | 式 |
{# #} | コメント |
# | 式(1行すべて式の場合。アプリケーション (本ページの場合app.py)側で app.jinja_env.line_statement_prefix = '#'の設定が必要 |
文字列の埋め込み
変数による文字列の埋め込みは下記の通り記載できます。変数には配列や辞書型も記載できます。
<body>
<div class="container">
<div class="header"> Web page</div>
<p>{{data1}}</p> # 変数
<p>{{data2.data}}</p> # 辞書型
<p>{{data3[0]}}</p> # 配列
</div>
</div>
</body>
ステートメント
Jinja2の記法を使ってif文やfor分を作ることもできます。またfor文の中で新たに変数を定義し、それらを変数として使うこともできます。
<body>
<div class="container">
<div class="header"> Web page</div>
<!-- if文の追加 -->
{% if data1=="hoge1" %}
<p>{{data1}}</p>
<p>{{data2.data}}</p>
{% endif %}
<!-- for文の追加 -->
{% for i in data3 %}
<p>{{i}}</p> <!-- for文の中の新規変数を利用可能 -->
{% endfor %}
</div>
</div>
</body>
また{% set %}で変数を設定したり、{% macro %}や{% call %}を使って、マクロを定義したり、それを呼び出したりすることができます。
<body>
<div class="container">
<div class="header"> Web page</div>
{% if data1=="hoge1" %}
<p>{{data1}}</p>
{% endif %}
<!-- setによる変数への格納 -->
{% set data1="hoge1-2" %}
<!-- line statementsによる記載(app.jinja_env.line_statement_prefix = '#'の設定が必要) -->
# if data1=="hoge1-2":
<p>{{data2.data}}</p>
# endif
<!-- macroによるマクロ追加 -->
{% macro show_data(data_list) %}
{% for i in data_list %}
<p>{{i}}</p>
{% endfor %}
{% endmacro %}
<!-- macroの実行 -->
<p>{{show_data(data3)}}</p>
<!-- callを使うことにより、macroを再利用しながら拡張することが可能 -->
{% macro show_data2(data_list) %}
{% for i in data_list %}
<p>{{caller()}}{{i}}</p> <!-- call呼び出し時にcallの中身をcaller()にバインド -->
{% endfor %}
{% endmacro %}
<!-- callでマクロを実行 -->
{% call show_data2(data3) %}
call:
{% endcall%}
</div>
</div>
</body>
{% block %}や{% include %}でほかのhtmlから情報を持ってくることができます。
title
<!-- blockでバインドするブロック箇所を指定 -->
{% block body %}{% endblock %}
<!-- extendsにて親のhtmlを指定 -->
{% extends 'base.html' %}
<!-- blockにて親のhtmlにバインドするブロックを記載 -->
{% block body%}
<body>
<div class="container">
<div class="header"> Web page</div>
<!-- includeにて他のhtmlを呼び出し -->
{% include 'include.html'%}
</div>
</div>
</body>
{% endblock %}
include include.html
フィルタ
Jinjaにはフィルタ機能というものが備わっていて、データに対して変換をかけることができます。フィルタ機能は"data|safe"のように、|(パイプ)を使って記載することが可能です。Buildin-Filterの一覧はこちらにあります。
<body>
<div class="container">
<div class="header"> Web page</div>
<p>{{data1}}</p>
<!-- length Filterを追加し、テキストの長さを出力 -->
<p>{{ data1|length }}</p>
</div>
</div>
</body>
エスケープ処理
FlaskではXSS(Cross Site Scripting)対策として、&,<,>,",'をエスケープ処理(無害な文字列への変換処理:ex "<" → "<" など)しています。これらを無効にする場合は{% autoescape false %}を使用します。
<body>
<div class="container">
<div class="header"> Web page</div>
<p>{{data1}}</p>
{% set data1="<script>alert('XSS');</script>" %}
{% autoescape false %}
<!-- この中で出力されるものについてはエスケープ処理されないため、タグなどが有効になります -->
<p>{{data1}}</p>
{% endautoescape %}
</div>
</div>
</body>
実行するとscriptタグの中のスクリプトが実行されます。
Flaskオブジェクトの表示
Flaskの固有のオブジェクトについてもテンプレートエンジンを使って表示させることが可能です。
<body>
<table>
<tr>
<td>config</td>
<td>{{config}}</td> # 現在のflaskのコンフィグオブジェクトを表示
</tr>
<tr>
<td>session</td>
<td>{{session}}</td> # 現在のflaskのセッションオブジェクトを表示
</tr>
</table>
</body>
以上、次回はデータベース周りとの連携について記載したいと思います。