よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

テンプレートエンジンとは何か?Jinja2の基本5

テンプレートエンジンJinja2単体の機能を見ていくシリーズ5回目。前回テンプレートエンジンとは何か?Jinja2の基本4 - よちよちpython の続き。
1,2回目でテンプレートエンジンとはどんなものかとテンプレートの書き方を、3,4回目でAPIの書き方をざっと見て来たが、今回はまたテンプレートの書き方を見ていく。まとめて書けよって感じだが。



参考 Jinja2 Template Designer Documentation
Template Designer Documentation — Jinja Documentation (2.11.x)



目次




実行環境


Androidスマホ
termux
Python3.7
JupyterNotebook

テンプレートの書き方 つづき


フィルター


フィルターによって変数の値を変更できる。
組み込まれたフィルターと別に、フィルターの自作もできる。
{{ 変数 | フィルター | フィルター(オプションの引数)}}のように書き、|パイプで各フィルターを連結可能。オプションで引数を含めることができる。

組み込みフィルター使用例 大文字に変換する upper


変数を大文字に変換するフィルター使用例



テンプレート作成

%%writefile template.j2

{{word1|upper()}} {{word2|upper()}} {{word3|upper()}} {{word4|upper()}} !
Overwriting template.j2

API作成

%%writefile app.py

from jinja2 import Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('template.j2')

# データは全て小文字
words = {"word1":"make","word2":"america","word3":"great","word4":"again"}
fool = template.render(words)

print(fool)
Overwriting app.py

実行

!python app.py
MAKE AMERICA GREAT AGAIN !



組み込みフィルター使用例2 アイテム数 length


APIは同じものを使い、テンプレートの変数のアイテム数を返すフィルターに変更する。

テンプレート作成

%%writefile template.j2

{{word1|length}} {{word2|length}} {{word3|length}} {{word4|length}} !
Overwriting template.j2

実行

!python app.py
4 7 5 5 !

シ ナ go go !
なるほど、暗号であったか。



自作フィルター使用例


フィルターを自作する場合は、APIファイルで関数とフィルターを作る。

API作成 自作フィルター

%%writefile app.py

# 自作フィルター関数定義
def my_filter(arg):
    """数値を2進数に変換"""
    return bin(arg)


from jinja2 import Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('.'),trim_blocks=True)

#自作フィルター設定
env.filters['my_filter'] = my_filter
template = env.get_template('template_2.j2')

#データ
numbers_dict = {'numbers':[6,2,10]}

b = template.render(numbers_dict)

print(b)
Overwriting app.py

テンプレート作成

%%writefile template_2.j2

{%- for num in numbers %}

{{num}}は2進数で {{num|my_filter}}

{%- endfor -%}
Overwriting template_2.j2
!python app.py
6は2進数で 0b110
2は2進数で 0b10
10は2進数で 0b1010



include インクルード


includeは英語で「含める」という意味です。複数に分かれたテンプレートを一つにまとめて出力したいようなときに使う。
あるテンプレートに{%include 別のテンプレート名%}を書くと、その名のテンプレートがそこにハメ込まれます。
テンプレートファイルを2つ作って確かめてみます。

API作成

%%writefile app.py

from jinja2 import Environment, FileSystemLoader

#includeを書いた方のテンプレートをロードする
env = Environment(loader=FileSystemLoader('.'),trim_blocks=False,lstrip_blocks=True)
template = env.get_template('template2.j2')

# データ
keyword = {'num':7}
text = template.render(keyword)

print(text)
Overwriting app.py

テンプレート1つ目作成

%%writefile template1.j2

★★★★★★★★★★★★★
こちらがテンプレート1つ目
★★★★★★★★★★★★★
Overwriting template1.j2

テンプレート2つ目
これにincludeを書いて1つ目をハメ込むとする。

%%writefile template2.j2

{%include 'template1.j2'%}
あなたは{{num}}番目のお客様です。
Writing template2.j2

実行

  1. template1.j2
  2. template2.j2
  3. app.py

の3つを作った。3で2をloadする。2にinclude文が書いてあり、1を入れ込む。

!python app.py
★★★★★★★★★★★★★
こちらがテンプレート1つ目
★★★★★★★★★★★★★
あなたは7番目のお客様です。

疑問 複数のテンプレートをloadできるのか?


上のテンプレートの1つ目はapp.pyでloadしていないので変数を使えないだろうと思って使わなかった。
両方のテンプレートに変数を使って、includeできるだろうか?
同じように3つのファイルを作成して実験してみる。



API作成

%%writefile app_new.py

from jinja2 import Environment, FileSystemLoader

#2つのテンプレートをロードする
env = Environment(loader=FileSystemLoader('.'),trim_blocks=False,lstrip_blocks=True)
template_1 = env.get_template('template_1.j2')
template_2 = env.get_template('template_2.j2')

# データ1
keyword_1 = {'name':'誉田和氣'}
text_1 = template_1.render(keyword_1)
#データ2
keyword_2 = {'item':'後漢鏡'}
text_2 = template_2.render(keyword_2)

print(text_2)
Overwriting app_new.py

テンプレート1つ目

%%writefile template_1.j2

お客様名 : {{name}}
Writing template_1.j2

テンプレート2つ目
こちらにincludeを書くとする。

%%writefile template_2.j2

{%include 'template_1.j2'%}
お買い上げ商品 : {{item}}
Overwriting template_2.j2

実行

!python app_new.py
お客様名 : 
お買い上げ商品 : 後漢鏡

この方法では上手く行きませんね。始めにtemplate_2をloadして、その中にあるinclude先のtemplate_1を含める。
その後に変数をレンダリングで置き換えてるようだ。



include文を削除して

print(text_1)
print(text_2)

とすれば両方のテンプレートの変数が置き換わったものが表示できることはできる。今後の課題として一旦放置で。



macro マクロ


テンプレート内に関数を定義することができる。
定義するときは{% macro マクロ名(引数リスト) %} … {% endmacro %}のように書く。
呼び出しは{{ マクロ名(実引数リスト) }}

サンプルを挙げるだけにしておく。 htmlにinputタグを組むマクロ。

{% macro input(name, value='', type='text', size=20) -%}
<input type="{{ type }}" name="{{ name }}" value="{{
    value|e }}" size="{{ size }}">
{%- endmacro %}
 
<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>



from jinja2 import Template, Environment, FileSystemLoader
 
env = Environment(loader=FileSystemLoader('.'), trim_blocks=False)
template = env.get_template('sample2.tpl')
disp_text = template.render()
print(disp_text)



<p><input type="text" name="username" value="" size="20"></p>
<p><input type="password" name="password" value="" size="20"></p>



import マクロのインポート


上でマクロをテンプレート内に書いたが、マクロだけを別のテンプレートファイルに書いて、そのマクロテンプレートファイルをテンプレートにインポートで呼び出して使うことができる。includeのマクロ版といった所でしょうか。
テンプレート内に{% import 'マクロテンプレート名' as 短縮名 %}と書いて、呼び出す時はPythonのようにモジュール名.関数名で使う。またテンプレートのサンプルを挙げるだけにしておく。

{% macro input(name, value='', type='text', size=20) -%}
<input type="{{ type }}" name="{{ name }}" value="{{
    value|e }}" size="{{ size }}">
{%- endmacro %}



{% import 'inputhelper.j2' as helper %}
<p>{{ helper.input('username') }}</p>
<p>{{ helper.input('password', type='password') }}</p>



テンプレートの継承


元のファイルの複製から「基本的には一緒なんだけどこの部分はオリジナルにしたい」、というとき、継承というテクニックで再利用し何度も同じ箇所を書く手間を省く。リンクで済ます省エネ志向(←ただの横着 & Pythonのクラス継承も理解できていない)

参考



最後に


Jinja2の基本的使い方を説明したいくつかのサイトで扱う内容は大体一通り書いた。それらのサイトの大抵がhtmlを出力する内容になっている。これぐらいの内容をサラッと扱えればWebページの生成ぐらいは一人でできるようになるかな?



以上をもちましてJinja2基本シリーズを終了させます!

参考にさせて頂きましたサイトの方々に感謝申し上げます。ありがとうございました。

参考ページ


Jinja — Jinja Documentation (2.11.x)



jinja2 | Python学習講座



Jinja2 利用ノート — 読書ノート v1.5dev



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