今天要來看 jinja 這個模板引擎。簡單來說,它的功能就是在 HTML 裡面執行一般程式,等等看過範例之後應該可以更清楚它的作用。
雖然說是在執行一般程式,但是其語法和一般程式也有一些些差別,我自己的習慣是照著一般程式的語法,如果錯了的話再去搜尋正確的用法。
這次並非只需要簡單地把程式碼複製到 app.py
而已,我們需要先建立一個名為 templates
的資料夾,名字必須完全符合,等等會需要把 HTML 及以後會用到的 jinja 模板放在裡面。
建立完資料夾後,我們來看看以下這份程式碼,請注意 index.html
需要放在 templates
目錄內。
app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index_page():
name = "cat"
return render_template("index.html", username=name)
app.run(host="127.0.0.1", port=8080, debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index</title>
</head>
<body>
{{ username }}
</body>
</html>
在 app.py
中,我們看到了一個新的函式:render_template
,不例外地,他也需要 import。它的功用是把一個 template 送入 jinja 跑,然後就可以得到一個字串,內容就是跑出來的 HTML,最後會把他 return 回去,這樣一來,使用者就可以收到一個漂亮的 HTML,而非之前連 head
都沒有的單純內容。有了它的好處是,我們不需要把超長的 HTML 放在 python 檔案裡面,而且可以用比較漂亮的方式生出 HTML,等等在看 for
迴圈的時候會更有感覺。
它需要一個 template name,以此處為例就是 index.html
。接下來會看到他後面的 username=name
,他會把剛剛定義的 name
傳入 template 給 jinja 處理,而在 jinja 裡面,他的名字叫做 username
。接著我們看到 index.html
,它裡面有一行 {{ username }}
,在 jinja 裡面,我們使用大括號來表示我們存取的變數,此處就是剛剛在 redner_template
傳入的 name
。
這邊值得注意的是,jinja 只會去處理大括號的部分,其他 HTML 的元素他秋毫無犯,會原汁原味秀出來。
剛剛看了基本的使用方法,現在來看看 if
要怎麼處理。
app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index_page():
state = "running"
return render_template("index.html", state=state)
app.run(host="127.0.0.1", port=8080, debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index</title>
</head>
<body>
{% if state == "running" %}
<p>running</p>
{% else %}
<p>terminated</p>
{% endif %}
</body>
</html>
我們這次傳入了 state
這個變數,然後在 jinja 裡面判斷他是否等於 running
。這裡要注意的是他 if 並不是跟剛剛一樣用兩個大括號,而是一個大括號和一個百分符號。這兩種的差別在於他是不是一個 statement,{% %}
是給 statement 用的,而 {{ }}
則是用來存取內容用的。還有跟 python 不太一樣的是他有 endif
,要記得加。也因為他已經有了 endif
,所以他不會管你的縮排。
還有一個要注意的是,我們的 <p>running</p>
和 <p>terminated</p>
並沒有加上大括號,因為他就是一般的 HTML element。如果要把 running
換成 state
,那就要改成 <p>{{ state }}</p>
。
接下來就要講迴圈,比較特別的是 jinja 只有 for 沒有 while,以下是一個範例。
app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index_page():
users = ["Mary", "Cat", "Meow", "Harry"]
return render_template("index.html", users=users)
app.run(host="127.0.0.1", port=8080, debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index</title>
</head>
<body>
<h1>User list</h1>
<ul>
{% for user in users %}
<li>{{ user | upper }} ({{ loop.index }})</li>
{% endfor %}
</ul>
</body>
</html>
在這個範例中我們傳入了一個叫做 users
的 list,接下來我們交給 jinja 處理,他幫我們跑一個 for 迴圈 (注意他也是 statement,所以要用 {% %}
),依序把 users
裡的 user
取出來,然後放在 li
裡面。接下來看到後面的 loop.index
,他是一個預設就存在的變數,他表示了平常在 list 看到的 index,但他是從 1
開始數,如果要從零開始數需要用 loop.index0
。
最後看到在 user
後面的 | upper
,他是一個 filter,就是等同於平常用到的 str.upper()
,只是在此處我們習慣使用 filter。在此處當然可以直接 {{ user.upper() }}
就好,但是有些時候沒有辦法用 python 的語法來處理,像是如果我們直接使用 int()
,他會告訴我們 undefined
,這時候就需要用 | int
來達成需求。
如果想要加入自定義的 filter 的話,flask 已經有一個包裝好的方法可以處理,以下是範例。
app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.template_filter()
def get_initial(s):
return s[0]
@app.route("/")
def index_page():
user = "Cat"
return render_template("index.html", user=user)
app.run(host="127.0.0.1", port=8080, debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index</title>
</head>
<body>
{{ user | get_initial }}
</body>
</html>
可以看到在 app.py
的最前面我們使用了 app.template_filter
,讓他裝飾了一個會丟出第一個字母的函式,接下來在 index.html
我們就直接使用了這個 filter,不需要把它當成參數放在 render_template
,也不需要特別 import,他就直接變成了預設就存在的 filter。
Basic Syntax of Jinja
Python jinja2 + flask
Custom Jinja template filters in Flask