BottleからJinja2を使ってみる
Kibaraです。
主な仕事は神社仏閣を巡りおみくじを引くことです。
ということで、今回はJinja2を学んでいきたいと思います。
こちらを参考にさせていただいております。
Jinja2 Docs
環境
軽量なWebフレームワークであるBottleからJinja2を利用します。
Pythonの環境は以下の通りです。
>python --version Python 3.7.1
インストール
bottle.pyは1ファイルなので、どこかから入手するか、
condaの場合はconda-forge
を利用します。
# インストール > conda install -c conda-forge bottle > conda install jinja2 # バージョン確認 >conda list bottle bottle 0.12.16 py_0 conda-forge >conda list jinja2 jinja2 2.10 py37_0
Jinja2とは
テンプレートエンジンです。
Webフレームワーク等からKey-Valueな形でデータを受け取り、
htmlに展開するような機能を持ちます。
例えば以下のように、htmlの中にPythonっぽいコードが書けます。
<html> <head> <title>{% title %}</title> </head> <body> <ul> {% for user in users %} <li><a href="{{ user.url }}">{{ user.username }}</a></li> {% endfor %} </ul> </body> </html>
上の例では、htmlのみだとユーザーの数だけリストを書かなければなりませんが、
users
でリスト型のデータを渡すことで、動的に生成することができます。
また、変数の中身が変われば、表示されるhtmlの内容も変わります。
ベースとなるデザインをhtml/cssで記述し、
メインのコンテンツはテンプレートエンジンで動的に生成する、
といったことが可能になります。
Jinja2の基本文法
基本的な書き方を見ておきます。
変数
{{ 変数名 }}
分岐
{% if 条件式 %} ~~~ {% elif 条件式 %} ~~~ {% endif %}
繰り返し
{% for 変数 in リスト %} ~~~ {% endfor %}
ブロック
{% block ブロック名 %} ~~~ {% endblock %}
コメント
{# コメント #}
これでプログラムの基本原則である、順次、分岐、反復を習得しました。
完璧ですね。もう敵はいない。
動かす
では、サクッと動かしてみます。
bottleでサーバを起動
早速テンプレートを…と言いたいところですが、
まずはbottleでサーバーを立ち上げます。
from bottle import Bottle, run from paste import httpserver as web app = Bottle() @app.route('/') def top(): return "top" run(app=app, host='127.0.0.1', port=8080, server='paste')
起動できました。
bottleはデフォルトでシングルスレッドですが、
serverにpasteを指定してマルチスレッドで動作させています。
今回の場合は特に意味はありません。(紹介のため)
シンプルなテンプレート
ベースとなるHTMLファイルと、テンプレートを記述するファイルを作成しましょう。
templateディレクトリを作成し、index.htmlとindex.j2を作成します。
まずはindex.htmlから記述します。
<!DOCTYPE html> <html> <head> <title>{{ title }}</title> </head> <body> <p>{{ content }}</p> </body> </html>
title
とcontent
という変数を定義しました。
テンプレートを通じてこの変数に値を与え、展開して表示させてあげます。
{% extends "index.html" %}
現時点ではテンプレートはこれだけです。
後で追記していきます。
最後に、bottleからデータを渡してあげましょう。
@app.route('/') def top(): context = {} context["title"] = "Jinja2Test" context["content"] = "top" return template('index.j2', **context)
テンプレートへ渡すデータを辞書型で作成し、
テンプレートファイル名とデータを指定してレスポンスしています。
ではレッツ実行。
title
とcontent
に指定した値が展開されていますね。
分岐と反復
Pythonから引数を与えて、HTMLに値を展開することができましたので、
次は分岐と反復を使ってコーディングしてみます。
Pythonからusers
にリスト型の値を入れてテンプレートへ渡します。
@app.route('/') def top(): context = {} context["title"] = "User List" context["users"] = ["Ayame","Itsuki","Utsugi"] return template('index.j2', **context)
値を受け取るHTML側は以下のようになります。
<body> <h1>{{ title }}</h1> {% if users|length == 0 %} <p>no user</p> {% else %} <ul> {% for user_name in users %} <li>{{ user_name }}</li> {% endfor %} </ul> {% endif %} </body>
users
があればユーザーの数だけリストで表示し、
無ければno user
と表示します。
users|length
とすることで、usersの要素数をカウントしています。
リスト型の値に対してできる操作は色々とあり、以下が参考になります。
List of Builtin Filters
ブロック
これまでの例ではHTMLファイルの中にコードを書いており、
テンプレートエンジンのメリットをあまり享受できていませんでした。
ブロックを使ってHTMLのひな形と実装を分離しましょう。
まずはhtmlファイルからロジック部分を削除し、content
という名前のブロックを作成します。
<body> <h1>{{ title }}</h1> {% block content %} {% endblock %} </body>
次に、テンプレートファイルにもcontent
という名前のブロックを作成し、
その内部に先ほどのロジック部分を記述します。
{% extends "index.html" %} {% block content %} {% if users|count == 0 %} <p>no user</p> {% else %} <ul> {% for user_name in users %} <li>{{ user_name }}</li> {% endfor %} </ul> {% endif %} {% endblock %}
Pythonの実装には変更ありません。
実行してみると、先ほどと同じ結果になっていると思います。
これで、HTMLのひな形とロジックの分離ができました。
同じひな形に異なるロジックを挿入する
冒頭で書いたように、ベースとなるデザインをHTML/CSSで記述し、
コンテンツをテンプレートで動的に生成するような使い方ができます。
index.htmlを使って、別のテンプレートのcontent
を描画させてみます。
まず、Bottleでアクセス先の指定と引数を作っておきます。
期末試験の結果をテーブル表示させてみましょう。
@app.route('/other') def other(): context = {} context["title"] = "期末試験の結果" context["headers"] = ["氏名","国語","数学","理科","社会","英語"] context["results"] = [ ["青木", 90, 80, 92, 88, 86], ["佐藤", 46, 22, 18, 55, 14], ["高木", 76, 62, 58, 69, 55]] return template('other.j2', **context)
テンプレート側です。
other.j2を作り、index.htmlを使うよう指定しました。
また、今回は簡単のためにStyle直書きですが、良い子はマネしないでくださいね。
{% extends "index.html" %} {% block content %} <table style="border: solid 2px black;border-collapse: collapse;"> {% for header in headers %} <th style="border: solid 1px black;">{{ header }}</th> {% endfor %} {% for res in results %} <tr> {% for data in res %} <td style="border: solid 1px black;">{{ data }}</td> {% endfor %} </tr> {% endfor %} </table> {% endblock %}
それでは、/other
にアクセスしてみます。
ばっちりテーブルを表示できています。
htmlファイルの骨組みはindex.htmlの1ファイルのみですが、
テンプレートを変更することで、内容を変化させることができました。
デザインを変える
index.htmlを修正することで、両方のページに修正を加えることができます。
たとえば、<h1>
の要素を赤色にするStyleを定義すれば、
User List
も期末試験の結果
も赤色になります。
まとめ
今回はテンプレートエンジンであるJinja2をBottleから使ってみました。
今回の例ではあまりメリットが伝わらなかったかもしれませんが、
htmlファイルを1つ修正すれば全ページのデザインが修正できると思うと、
大規模な構成において真価を発揮することがわかります。
PythonでWeb開発にもチャレンジしたいですね。
以上です。