Help us understand the problem. What is going on with this article?

素人がWebアプリケーション制作の過程を書いてみる(Flask テンプレートエンジン)

More than 1 year has passed since last update.

今回はFlaskのテンプレートエンジンを使ってみたいと思います。FlaskではJinja2のテンプレートエンジンを使用しています。Jinja2でのテンプレートとは"{%%}"や"{{}}"などの記法を用いて作成され、それらを活用して、文字列の埋め込み、if分岐などを実現しているものです。FlaskではサーバでのHTMLのレンダリングに使用されています。

Flaskへの実装

まずは前回のindex.htmlおよびapp.pyを変更します。

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 = '#'の設定が必要

文字列の埋め込み

変数による文字列の埋め込みは下記の通り記載できます。変数には配列や辞書型も記載できます。

index.html
<body>
    <div class="container">
        <div class="header"> Web page</div>
                <p>{{data1}}</p>     # 変数
                <p>{{data2.data}}</p>  # 辞書型
                <p>{{data3[0]}}</p>   # 配列
        </div>
    </div>
</body>

実行結果は下記のとおりです。
image.png

ステートメント

Jinja2の記法を使ってif文やfor分を作ることもできます。またfor文の中で新たに変数を定義し、それらを変数として使うこともできます。

index.html
<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>

下記が実行結果になります。
image.png

また{% set %}で変数を設定したり、{% macro %}や{% call %}を使って、マクロを定義したり、それを呼び出したりすることができます。

index.html
<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>

実行結果は下記の通りになります。
image.png

{% block %}や{% include %}でほかのhtmlから情報を持ってくることができます。

base.html
title
<!-- blockでバインドするブロック箇所を指定 -->
{% block body %}{% endblock %}
index.html
<!-- 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.html
include include.html

下記実行結果です。
image.png

フィルタ

Jinjaにはフィルタ機能というものが備わっていて、データに対して変換をかけることができます。フィルタ機能は"data|safe"のように、|(パイプ)を使って記載することが可能です。Buildin-Filterの一覧はこちらにあります。

index.html
<body>
    <div class="container">
        <div class="header"> Web page</div>
                <p>{{data1}}</p> 
                <!-- length Filterを追加し、テキストの長さを出力 -->
                <p>{{ data1|length }}</p> 
        </div>
    </div>
</body>

image.png

エスケープ処理

FlaskではXSS(Cross Site Scripting)対策として、&,<,>,",'をエスケープ処理(無害な文字列への変換処理:ex "<" → "&lt" など)しています。これらを無効にする場合は{% autoescape false %}を使用します。

index.html
<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タグの中のスクリプトが実行されます。

image.png

Flaskオブジェクトの表示

Flaskの固有のオブジェクトについてもテンプレートエンジンを使って表示させることが可能です。

index.html
<body>
    <table>
        <tr>
            <td>config</td>
            <td>{{config}}</td>   # 現在のflaskのコンフィグオブジェクトを表示
        </tr>
        <tr>
            <td>session</td>
            <td>{{session}}</td>   # 現在のflaskのセッションオブジェクトを表示
        </tr>
    </table>
</body>

ブラウザでアクセスすると結果は下記のようになります。
image.png

以上、次回はデータベース周りとの連携について記載したいと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした