Flaskアプリケーションでテンプレートを使用する方法

著者は、Write for DOnationsプログラムの一環として、Free and Open Source Fundに寄付を行うことを選択しました。

はじめに

Flaskは、Python言語でWebアプリケーションを作成するための便利なツールや機能を提供する、軽量なPython Webフレームワークです。

Webアプリケーションを開発する際、ビジネスロジックプレゼンテーションロジックを分離することが重要です。ビジネスロジックは、ユーザーのリクエストを処理し、データベースと通信して適切なレスポンスを構築するものです。プレゼンテーションロジックは、データがユーザーにどのように提示されるかであり、通常、HTMLファイルを使用してレスポンスページの基本構造を構築し、CSSスタイルを使用してHTMLコンポーネントのスタイルを設定します。例えば、ソーシャルメディアアプリケーションでは、ユーザーがログインしていない場合にのみ、ユーザー名フィールドとパスワードフィールドを表示することができます。ユーザーがログインしている場合は、代わりにログアウトボタンを表示します。これがプレゼンテーションロジックです。ユーザーがユーザー名とパスワードを入力した場合、Flaskを使用してビジネスロジックを実行できます:リクエストからデータ(ユーザー名とパスワード)を抽出し、資格情報が正しければユーザーをログインさせるか、エラーメッセージで応答します。エラーメッセージの表示方法は、プレゼンテーションロジックによって処理されます。

Flaskでは、Jinjaテンプレート言語を使用してHTMLテンプレートをレンダリングできます。テンプレートは、固定されたコンテンツと動的なコンテンツの両方を含むことができるファイルです。ユーザーがアプリケーションから何かを要求するとき(インデックスページやログインページなど)、Jinjaを使用すると、標準のHTMLでは利用できない多くの機能、例えば変数、if文、forループ、フィルター、テンプレートの継承などを使用できるHTMLテンプレートで応答できます。これらの機能により、メンテナンスが容易なHTMLページを効率的に記述できます。Jinjaはまた、クロスサイトスクリプティング(XSS)攻撃を防ぐためにHTMLを自動的にエスケープします。

このチュートリアルでは、いくつかのHTMLファイルをレンダリングする小さなWebアプリケーションを構築します。サーバーからテンプレートにデータを渡すために変数を使用します。テンプレートの継承は、繰り返しを避けるのに役立ちます。条件文やループなどのテンプレート内のロジックを使用し、テキストを変更するためにフィルターを使用し、Bootstrapツールキットを使用してアプリケーションのスタイルを設定します。

前提条件

ステップ1 — テンプレートのレンダリングと変数の使用

環境がアクティブ化され、Flaskがインストールされていることを確認してから、アプリケーションの構築を開始できます。最初のステップは、インデックスページに訪問者を歓迎するメッセージを表示することです。Flaskのrender_template()ヘルパー関数を使用して、HTMLテンプレートをレスポンスとして提供します。また、アプリケーション側からテンプレートに変数を渡す方法も見ていきます。

まず、flask_appディレクトリで、app.pyという名前のファイルを編集用に開きます。nanoまたはお好みのテキストエディタを使用してください:

  1. nano app.py

app.pyファイル内に以下のコードを追加します:

flask_app/app.py

from flask import Flask, render_template

app = Flask(__name__)


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

ファイルを保存して閉じます。

このコードブロックでは、Flaskクラスとrender_template()関数をflaskパッケージからインポートします。Flaskクラスを使用して、appという名前のFlaskアプリケーションインスタンスを作成します。次に、app.route()デコレータを使用して、ビュー関数(HTTPレスポンスを返すPython関数)としてhello()という関数を定義します。このビュー関数は、render_template()関数を使用してindex.htmlというテンプレートファイルをレンダリングします。

次に、flask_appディレクトリ内にあるtemplatesというディレクトリにindex.htmlテンプレートファイルを作成する必要があります。Flaskはtemplatesディレクトリという名前のtemplatesディレクトリ内のテンプレートを探すため、この名前は重要です。flask_appディレクトリ内にいることを確認し、以下のコマンドを実行してtemplatesディレクトリを作成してください:

  1. mkdir templates

次に、templatesディレクトリ内にあるindex.htmlというファイルを編集用に開いてください。ここでのindex.htmlという名前は標準的な必須名ではありません。home.htmlhomepage.htmlなど、好きな名前を付けることができます:

  1. nano templates/index.html

index.htmlファイル内に以下のHTMLコードを追加してください:

flask_app/templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FlaskApp</title>
</head>
<body>
    <h1>Hello World!</h1>
    <h2>Welcome to FlaskApp!</h2>
</body>
</html>

ここでは、タイトルを設定し、Hello World!メッセージをH1見出しとして追加し、Welcome to FlaskApp!メッセージをH2見出しとして作成しました。

ファイルを保存して閉じます。

仮想環境が有効化されたflask_appディレクトリ内で、Flaskにアプリケーション(app.py)について知らせ、FLASK_ENV環境変数をdevelopmentに設定して開発モードでアプリケーションを実行し、デバッガにアクセスできるようにします。これを行うには、以下のコマンドを使用してください(Windowsの場合、exportの代わりにsetを使用します):

  1. export FLASK_APP=app
  2. export FLASK_ENV=development

次に、flask runコマンドを使用してアプリケーションを実行します:

  1. flask run

開発サーバーが実行されている状態で、ブラウザを使用して以下のURLにアクセスしてください:

http://127.0.0.1:5000/

ページのタイトルがFlaskAppに設定されており、2つの見出しはHTMLとしてレンダリングされています。

ウェブアプリケーションでは、アプリケーションのPythonファイルからHTMLテンプレートにデータを渡す必要がよくあります。このアプリケーションでその方法を示すために、現在のUTC日時を含む変数をインデックステンプレートに渡し、その変数の値をテンプレートに表示します。

サーバーを稼働させたまま、新しいターミナルでapp.pyファイルを編集用に開いてください:

  1. nano app.py

Pythonの標準ライブラリからdatetimeモジュールをインポートし、index()関数を以下のように編集してください:

flask_app/app.py
import datetime
from flask import Flask, render_template

app = Flask(__name__)


@app.route('/')
def hello():
    return render_template('index.html', utc_dt=datetime.datetime.utcnow())

ファイルを保存して閉じます。

ここではdatetimeモジュールをインポートし、utc_dtという変数をindex.htmlテンプレートにdatetime.datetime.utcnow()の値、つまり現在のUTC日時で渡しました。

次に、変数の値をインデックスページに表示するために、index.htmlファイルを編集用に開いてください:

  1. nano templates/index.html

ファイルを以下のように編集してください:

flask_app/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FlaskApp</title>
</head>
<body>
    <h1>Hello World!</h1>
    <h2>Welcome to FlaskApp!</h2>
    <h3>{{ utc_dt }}</h3>
</body>
</html>

ファイルを保存して閉じます。

特別な{{ ... }}デリミタを使ってutc_dt変数の値を表示するH3見出しを追加しました。

ブラウザを開いてインデックスページにアクセスしてください:

http://127.0.0.1:5000/

次の画像のようなページが表示されます:

インデックスページをHTMLテンプレートで作成し、テンプレートをレンダリングし、変数の値を渡して表示しました。次に、テンプレートの継承を使用してコードの繰り返しを避けます。

ステップ2 — テンプレートの継承を使用する

このステップでは、他のテンプレートと共有できるコンテンツを含む基本テンプレートを作成します。インデックステンプレートを編集して基本テンプレートを継承するようにします。そして、アプリケーションの「About」ページとして機能する新しいページを作成し、ユーザーはアプリケーションに関する詳細情報を見つけることができます。

基本テンプレートには、アプリケーションのタイトル、ナビゲーションバー、フッターなど、他のすべてのテンプレート間で共有されるHTMLコンポーネントが含まれています。

まず、テンプレートディレクトリ内にbase.htmlという新しいファイルを開いて編集します。

  1. nano templates/base.html

以下のコードをbase.htmlファイル内に記述します。

flask_app/templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }
    </style>
</head>
<body>
    <nav>
        <a href="#">FlaskApp</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

ファイルを保存して閉じます。

このファイルのほとんどのコードは標準的なHTMLで、タイトル、ナビゲーションリンクのスタイル、インデックスページとまだ作成されていないAboutページへのリンクを持つナビゲーションバー、そしてページのコンテンツ用の<div>があります(リンクはまだ機能しません。次のステップでページ間のリンク方法を示します)。

ただし、以下のハイライトされた部分はJinjaテンプレートエンジンに固有のものです。

  • {% block title %} {% endblock %}: タイトルのプレースホルダーとして機能するブロックです。後で他のテンプレートで使用して、毎回<head>セクション全体を書き直すことなく、アプリケーションの各ページにカスタムタイトルを提供できます。

  • {% block content %} {% endblock %}: 子テンプレートbase.htmlから継承するテンプレート)によってオーバーライドされるコンテンツに置き換えられる別のブロックです。

ベーステンプレートができたので、継承を利用して活用できます。index.htmlファイルを開いてください:

  1. nano templates/index.html

そして、その内容を以下のように置き換えます:

flask_app/templates/index.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Index {% endblock %}</h1>
    <h1>Hello World!</h1>
    <h2>Welcome to FlaskApp!</h2>
    <h3>{{ utc_dt }}</h3>
{% endblock %}

ここでは、{% extends %}タグを使用してbase.htmlテンプレートを継承します。その後、前のコードブロックのcontentブロック内の内容で、ベーステンプレートのcontentブロックを置き換えることで拡張します。

このコンテンツブロックには、タイトルブロック内にIndexというテキストを持つ<h1>タグが含まれており、元のbase.htmlテンプレートのtitleブロックをIndexというテキストで置き換えることで、完全なタイトルがIndex - FlaskAppになります。この方法により、同じテキストを二重に記述することを避けることができ、ページのタイトルとしても、ベーステンプレートから継承されたナビゲーションバーの下に表示される見出しとしても機能します。

その後、いくつかの見出しがあります:Hello World!というテキストを持つ<h1>見出し、<h2>見出し、そしてutc_dt変数の値を含む<h3>見出しです。

テンプレートの継承により、他のテンプレート(この場合はbase.html)でHTMLコードを再利用でき、必要なたびに繰り返す必要がなくなります。

ファイルを保存して閉じ、ブラウザでインデックスページを更新します。ページは以下のように表示されます:

次に、Aboutページを作成します。app.pyファイルを開いて新しいルートを追加します:

  1. nano app.py

ファイルの最後に以下のルートを追加します:

flask_app/app.py

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

ここでは、app.route()デコレータを使用して、about()というビュー関数を作成します。その中で、about.htmlテンプレートファイル名を引数としてrender_template()関数を呼び出した結果を返します。

ファイルを保存して閉じます。

編集のためにabout.htmlというテンプレートファイルを開きます。

  1. nano templates/about.html

ファイルに以下のコードを追加します。

flask_app/templates/about.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} About {% endblock %}</h1>
    <h3>FlaskApp is a Flask web application written in Python.</h3>
{% endblock %}

ここでは、extendsタグを使用して基本テンプレートを継承し、基本テンプレートのcontentブロックをページのタイトルとしても機能する<h1>タグで置き換え、アプリケーションに関する情報を含む<h3>タグを追加します。

ファイルを保存して閉じます。

開発サーバーが実行中の状態で、ブラウザを使用して以下のURLにアクセスします。

http://127.0.0.1:5000/about

次のようなページが表示されます。

ナビゲーションバーとタイトルの一部が基本テンプレートから継承されていることに注意してください。

これで、基本テンプレートを作成し、インデックスページとaboutページで使用してコードの重複を避けることができました。現時点では、ナビゲーションバーのリンクは何もしません。次のステップでは、ナビゲーションバーのリンクを修正してテンプレート内のルート間をリンクする方法を学びます。

ステップ3 — ページ間のリンク

このステップでは、url_for() ヘルパー関数を使用してテンプレート内のページ間をリンクする方法を学びます。ベーステンプレートのナビゲーションバーに、インデックスページとAboutページへの2つのリンクを追加します。

まず、ベーステンプレートを編集用に開いてください:

  1. nano templates/base.html

ファイルを以下のように編集します:

flask_app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }
    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('hello') }}">FlaskApp</a>
        <a href="{{ url_for('about') }}">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

ここでは、指定したビュー関数のURLを返す特殊な url_for() 関数を使用しています。最初のリンクは hello() ビュー関数のルート(インデックスページ)にリンクしています。2番目のリンクは about() ビュー関数のルートにリンクしています。ルート(/ または /about)ではなく、ビュー関数の名前を渡すことに注意してください。

url_for() 関数を使用してURLを構築することで、URLの管理が容易になります。URLをハードコーディングすると、ルートを編集した際にリンクが壊れる可能性があります。url_for() を使用することで、ルートを編集してもリンクが期待通りに機能することが保証されます。url_for() 関数は、特殊文字のエスケープなどの他の処理も行います。

ファイルを保存して閉じます。

次に、インデックスページに移動し、ナビゲーションバーのリンクを試してみてください。期待通りに機能することがわかります。

テンプレート内の他のルートにリンクするために url_for() 関数の使い方を学びました。次に、設定した条件に応じてテンプレートに表示される内容を制御するための条件文を追加し、テンプレート内で for ループを使用してリスト項目を表示します。

ステップ 4 — 条件文とループの使用

このステップでは、テンプレート内で if 文を使用して、特定の条件に応じて表示内容を制御します。また、Python のリストを通して for ループを使用し、リスト内の各項目を表示します。コメントをリストで表示する新しいページを追加します。偶数インデックス番号のコメントは青い背景で、奇数インデックス番号のコメントは灰色の背景で表示されます。

まず、コメントページのルートを作成します。app.py ファイルを編集用に開きます:

  1. nano app.py

ファイルの末尾に以下のルートを追加します:

flask_app/app.py

# ...

@app.route('/comments/')
def comments():
    comments = ['This is the first comment.',
                'This is the second comment.',
                'This is the third comment.',
                'This is the fourth comment.'
                ]

    return render_template('comments.html', comments=comments)

上記のルートでは、4つのアイテムを含む comments というPythonリストがあります。(これらのコメントは、通常、ここで行ったようにハードコードされるのではなく、実際のシナリオではデータベースから取得されます。)最後の行で、comments.html というテンプレートファイルを返し、リストを含む comments という変数をテンプレートファイルに渡します。

ファイルを保存して閉じます。

次に、templatesディレクトリ内に新しいcomments.htmlファイルを開いて編集します:

  1. nano templates/comments.html

ファイルに以下のコードを追加します:

flask_app/templates/comments.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
        <div style="padding: 10px; background-color: #EEE; margin: 20px">
            <p style="font-size: 24px">{{ comment }}</p>
        </div>
        {% endfor %}
    </div>
{% endblock %}

ここでは、base.htmlテンプレートを拡張し、contentブロックの内容を置き換えます。まず、ページのタイトルとしても機能する<h1>見出しを使用します。

{% for comment in comments %}行でJinjaのforループを使用して、commentsリスト内の各コメントを(comment変数に格納される)を順番に処理します。通常Jinjaで変数を表示するのと同じ方法で、<p style="font-size: 24px">{{ comment }}</p>タグ内にコメントを表示します。forループの終了を{% endfor %}キーワードで示します。これはPythonのforループとは異なり、Jinjaテンプレートでは特別なインデントがないためです。

ファイルを保存して閉じます。

開発サーバーを実行している状態で、ブラウザを開いてコメントページにアクセスします:

http://127.0.0.1:5000/comments

次のようなページが表示されます:

ここで、if条件文を使用して、インデックス番号が奇数のコメントにはグレーの背景を、偶数のコメントには青の背景を表示します。

comments.htmlテンプレートファイルを開いて編集します:

  1. nano templates/comments.html

以下のように編集します:

flask_app/templates/comments.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            {% if loop.index % 2 == 0 %}
                {% set bg_color = '#e6f9ff' %}
            {% else %}
                {% set bg_color = '#eee' %}
            {% endif %}

            <div style="padding: 10px; background-color: {{ bg_color }}; margin: 20px">
                <p>#{{ loop.index }}</p>
                <p style="font-size: 24px">{{ comment }}</p>
            </div>
        {% endfor %}
    </div>
{% endblock %}

この新しい編集で、{% if loop.index % 2 == 0 %} の行に if 文を追加しました。loop 変数は、Jinjaの特別な変数で、現在のループに関する情報にアクセスできます。ここでは、loop.index を使用して現在のアイテムのインデックスを取得していますが、これはPythonのリストのように 0 からではなく、1 から始まります。

この if 文は、インデックスが偶数かどうかを % 演算子を使ってチェックしています。インデックス番号を 2 で割った余りを調べ、余りが 0 であればインデックス番号は偶数、そうでなければ奇数と判断します。{% set %} タグを使って bg_color という変数を宣言します。インデックス番号が偶数の場合は青みがかった色に設定し、奇数の場合は bg_color 変数をグレーに設定します。そして、コメントを含む <div> タグの背景色に bg_color 変数を使用します。コメントのテキストの上に、loop.index を使って現在のインデックス番号を <p> タグに表示します。

ファイルを保存して閉じます。

ブラウザを開いてコメントページにアクセスします:

http://127.0.0.1:5000/comments

新しいコメントページが表示されます:

これは if 文の使い方のデモンストレーションでした。しかし、同じ効果を得るために特別な loop.cycle() Jinja ヘルパーを使用することもできます。これを実証するために、comments.html ファイルを開いてください:

  1. nano templates/comments.html
flask_app/templates/comments.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            <div style="padding: 10px;
                        background-color: {{ loop.cycle('#EEE', '#e6f9ff') }};
                        margin: 20px">
                <p>#{{ loop.index }}</p>
                <p style="font-size: 24px">{{ comment }}</p>
            </div>
        {% endfor %}
    </div>
{% endblock %}

ここで、if/else文を削除し、loop.cycle('#EEE', '#e6f9ff')ヘルパーを使用して2つの色を交互に切り替えます。background-colorの値は、一度は#EEE、次に#e6f9ffとなります。

ファイルを保存して閉じます。

ブラウザでコメントページを開き、更新すると、これがif文と同じ効果を持つことがわかります。

if文は、ページに表示される内容を制御するなど、複数の目的で使用できます。例えば、2番目のコメントを除くすべてのコメントを表示するには、条件loop.index != 2を持つif文を使用して2番目のコメントを除外します。

コメントテンプレートを開きます:

  1. nano templates/comments.html

そして、次のように編集します:

flask_app/templates/comments.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            {% if loop.index != 2 %}
                <div style="padding: 10px;
                            background-color: #EEE;
                            margin: 20px">
                    <p>#{{ loop.index }}</p>
                    <p style="font-size: 24px">{{ comment }}</p>
                </div>
            {% endif %}
        {% endfor %}
    </div>
{% endblock %}

ここで、{% if loop.index != 2 %}を使用して、インデックスが2でないコメントのみを表示します。これは、2番目のコメントを除くすべてのコメントを意味します。また、背景色にハードコードされた値を使用し、loop.cycle()ヘルパーの代わりに単純化します。残りは変更されておらず、if文は{% endif %}で終了します。

ファイルを保存して閉じます。

コメントページを更新すると、2番目のコメントが表示されないことがわかります。

次に、ナビゲーションバーにコメントページへのリンクを追加する必要があります。ベーステンプレートを編集用に開きます:

  1. nano templates/base.html

<nav>タグの内容を編集し、新しい<a>リンクを追加します:

flask_app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }
    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('hello') }}">FlaskApp</a>
        <a href="{{ url_for('comments') }}">Comments</a>
        <a href="{{ url_for('about') }}">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

ここで、url_for()ヘルパーを使用してcomments()ビュー関数へリンクします。

ファイルを保存して閉じます。

ナビゲーションバーには、コメントページへリンクする新しいリンクが表示されるようになります。

テンプレートでif文を使用して特定の条件に応じて表示内容を制御し、forループを使用してPythonリストを通過し、リスト内の各アイテムを表示する方法を学びました。また、Jinjaの特別なloop変数についても学びました。次に、変数データの表示方法を制御するためにJinjaフィルタを使用します。

ステップ5 — フィルタの使用

このステップでは、テンプレートでJinjaフィルタを使用する方法を学びます。前のステップで追加したコメントを大文字に変換するためにupperフィルタを使用し、文字列のシーケンスを1つの文字列に結合するためにjoinフィルタを使用します。また、safeフィルタを使用して信頼できるHTMLコードをエスケープせずにレンダリングする方法も学びます。

まず、コメントページのコメントを大文字に変換します。comments.htmlテンプレートを編集用に開きます:

  1. nano templates/comments.html

以下のように編集します:

flask_app/templates/comments.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            {% if loop.index != 2 %}
                <div style="padding: 10px;
                            background-color: #EEE;
                            margin: 20px">
                    <p>#{{ loop.index }}</p>
                    <p style="font-size: 24px">{{ comment | upper }}</p>
                </div>
            {% endif %}
        {% endfor %}
    </div>
{% endblock %}

ここで、パイプ記号(|)を使用してupper フィルターを追加しました。これにより、comment変数の値が大文字に変更されます。

ファイルを保存して閉じます。

開発サーバーが実行中の状態で、ブラウザでコメントページを開いてください:

http://127.0.0.1:5000/comments

フィルターを適用した後、すべてのコメントが大文字になっていることがわかります。

フィルターは括弧内に引数を取ることもできます。これを実証するために、joinフィルターを使用してcommentsリスト内のすべてのコメントを結合します。

コメントテンプレートを開いてください:

  1. nano templates/comments.html

次のように編集します:

flask_app/templates/comments.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            {% if loop.index != 2 %}
                <div style="padding: 10px;
                            background-color: #EEE;
                            margin: 20px">
                    <p>#{{ loop.index }}</p>
                    <p style="font-size: 24px">{{ comment | upper }}</p>
                </div>
            {% endif %}
        {% endfor %}
        <hr>
        <div>
            <p>{{ comments | join(" | ") }}</p>
        </div>
    </div>
{% endblock %}

ここで、<hr>タグと<div>タグを追加し、join()フィルターを使用してcommentsリスト内のすべてのコメントを結合しました。

ファイルを保存して閉じます。

コメントページを更新すると、次のようなページが表示されます:

ご覧のように、commentsリストがパイプ記号で区切られて表示されており、これはjoin()フィルターに渡したものです。

もう一つ重要なフィルターはsafeフィルターで、これにより信頼できるHTMLをブラウザ上にレンダリングすることができます。これを説明するために、{{ }}というJinjaの区切り文字を使って、コメントテンプレートにHTMLタグを含むテキストを追加します。実際のシナリオでは、これはサーバーから変数として渡されます。次に、join()の引数をパイプ記号の代わりに<hr>タグに変更します。

コメントテンプレートを開きます:

  1. nano templates/comments.html

以下のように編集します:

flask_app/templates/comments.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            {% if loop.index != 2 %}
                <div style="padding: 10px;
                            background-color: #EEE;
                            margin: 20px">
                    <p>#{{ loop.index }}</p>
                    <p style="font-size: 24px">{{ comment | upper }}</p>
                </div>
            {% endif %}
        {% endfor %}
        <hr>
        <div>
            {{ "<h1>COMMENTS</h1>" }}
            <p>{{ comments | join(" <hr> ") }}</p>
        </div>
    </div>
{% endblock %}

ここでは、"<h1>COMMENTS</h1>"という値を追加し、joinの引数を<hr>タグに変更しました。

ファイルを保存して閉じます。

コメントページを更新すると、次のようなページが表示されます:

ご覧の通り、HTMLタグはレンダリングされませんでした。これはJinjaのセーフティ機能で、一部のHTMLタグは有害であり、クロスサイトスクリプティング(XSS)攻撃につながる可能性があるためです。信頼できるHTMLのみをブラウザにレンダリングするべきです。

上記のHTMLタグをレンダリングするために、コメントテンプレートファイルを開きます:

  1. nano templates/comments.html

それをsafeフィルターを追加して編集します:

flask_app/templates/comments.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Comments {% endblock %}</h1>
    <div style="width: 50%; margin: auto">
        {% for comment in comments %}
            {% if loop.index != 2 %}
                <div style="padding: 10px;
                            background-color: #EEE;
                            margin: 20px">
                    <p>#{{ loop.index }}</p>
                    <p style="font-size: 24px">{{ comment | upper }}</p>
                </div>
            {% endif %}
        {% endfor %}
        <hr>
        <div>
            {{ "<h1>COMMENTS</h1>" | safe }}
            <p>{{ comments | join(" <hr> ") | safe }}</p>
        </div>
    </div>
{% endblock %}

また、<p>{{ comments | join(" <hr> ") | safe }}</p>のように、フィルターを連鎖させることもできます。各フィルターは前のフィルタリングの結果に適用されます。

ファイルを保存して閉じます。

コメントページを更新すると、HTMLタグが期待通りにレンダリングされていることがわかります:

警告: 未知データソースからのHTMLにsafeフィルターを使用すると、アプリケーションがXSS攻撃に対して脆弱になる可能性があります。レンダリングするHTMLが信頼できるソースからのものでない限り、使用しないでください。

詳細については、組み込みのJinjaフィルターのリストを確認してください。

これで、Jinjaテンプレートでフィルターを使用して変数値を変更する方法を学びました。次に、Bootstrapツールキットを統合してアプリケーションのスタイルを設定します。

ステップ6 — Bootstrapの統合

このステップでは、Bootstrapツールキットを使用してアプリケーションのスタイルを設定する方法を学びます。ベーステンプレートにBootstrapのナビゲーションバーを追加し、ベーステンプレートから継承するすべてのページに表示されるようにします。

Bootstrapツールキットは、アプリケーションの視覚的な魅力を高めるのに役立ちます。また、レスポンシブウェブページをウェブアプリケーションに組み込むのにも役立ち、モバイルブラウザでも適切に機能します。これらの目標を達成するために独自のHTML、CSS、JavaScriptコードを書く必要はありません。

Bootstrapを使用するには、ベーステンプレートに追加して、他のすべてのテンプレートで使用できるようにする必要があります。

base.htmlテンプレートを編集するために開きます:

  1. nano templates/base.html

次のように編集してください:

flask_app/templates/base.html
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">

    <title>{% block title %} {% endblock %} - FlaskApp</title>
  </head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
        <a class="navbar-brand" href="{{ url_for('hello') }}">FlaskApp</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link" href="{{ url_for('comments') }}">Comments</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="{{ url_for('about') }}">About</a>
            </li>
        </ul>
        </div>
    </div>
    </nav>
    <div class="container">
        {% block content %} {% endblock %}
    </div>

    <!-- Optional JavaScript -->

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>

  </body>
</html>

上記のコードの大部分は、Bootstrapを使用するために必要なボイラープレートです。いくつかのメタタグがあり、<head>セクションにBootstrapのCSSファイルへのリンクがあります。また、下部にはオプションのJavaScriptへのリンクがあります。コードの強調表示部分には、前のステップで説明したJinjaコードが含まれています。特定のタグとCSSクラスを使用して、Bootstrapに各要素の表示方法を指示する方法に注目してください。

上記の<nav>タグ内には、クラスnavbar-brandを持つ<a>タグがあり、これはナビゲーションバー内のブランドリンクを決定します。<ul class="navbar-nav">タグ内には、<li>タグ内の<a>タグ内に通常のナビゲーションバーの項目があります。

これらのタグとCSSクラスについてもっと学ぶには、Bootstrapコンポーネントを参照してください。

ファイルを保存して閉じます。

開発サーバーが実行中の状態で、ブラウザでインデックスページを開きます:

http://127.0.0.1:5000/

次のようなページが表示されます:

これで、すべてのテンプレートでFlaskアプリケーションのアイテムをBootstrapコンポーネントを使ってスタイルすることができます。

結論

HTMLテンプレートをFlaskウェブアプリケーションで使用する方法を理解しました。サーバーからテンプレートにデータを渡すために変数を使用し、テンプレート継承を利用して繰り返しを避け、if条件文やforループなどの要素を取り入れ、異なるページ間をリンクさせました。テキストを変更して信頼できるHTMLを表示するためのフィルターについて学び、Bootstrapをアプリケーションに統合しました。

Flaskについてさらに詳しく知りたい場合は、Flaskトピックページをチェックしてください。

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-templates-in-a-flask-application