iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
Modern Web

Flask系列 第 7

Day 7 jinja (2)

前言

今天要繼續看 jinja 的使用,昨天的內容比較接近一般的變數存取、if、for 的使用,而今天的內容則是類似自定義函數。

範例

先來看個範例。請注意 index.htmlmsg.jinja 都要放在昨天提到的 templates/ 裡面。

app.py

from flask import Flask, render_template, flash

app = Flask(__name__)

@app.route("/")
def index_page():
    users = ["Mary", "Cat", "Meow", "Harry"]
    flash("meow", category="cat")
    return render_template("index.html", users=users)


app.config["SECRET_KEY"] = "rc498mt6848"
app.run(host="127.0.0.1", port=8080, debug=True)

index.html

{% from 'msg.jinja' import display_msg %}

<!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>
    {{ display_msg(get_flashed_messages(with_categories=True)) }}
    <h1>User list</h1>
    <ul>
        {% for user in users %}
        <li>{{ user }} ({{ loop.index }})</li>
        {% endfor %}
    </ul>
</body>
</html>

msg.jinja

{% macro display_msg(messages) %}
<div id="messages">
    {% if messages %}
    {% for category, message in messages %}
        <div class="{{ category }}">
            <span>{{ message }}</span>
        </div>
    {% endfor %}
    {% endif %}
</div>
{% endmacro %}

這次的檔案比較多,也比較繁雜,我們一個一個慢慢看。

app.py 基本上就是昨天用來解釋 for 的範例,但是新加入了一個叫做 flash 的函式 ,它的用途是把後端的資料送到前端顯示。比如說可能使用者在註冊帳號的時候,使用者名稱已經被用過了,那後端就可以發一條警告訊息到前端通知使用者這個名稱被使用了。雖然這裡說是前端,但基本上它也是由 jinja 去跑的,所以需要用 render_template。在他的後面我們加了一個 category 的參數,這是選用,它的功能就是放一個標籤給這條訊息,讓前端可以因應,例如說上面的名稱已被使用可能就是 warning,而註冊成功可能就是 success 之類的,在下面我們會用到它。

這裡還有一個需要注意的是,有一行 app.config["SECRET_KEY"] = "rc498mt6848"app.config 是這個 app 的設定檔,未來會有更舒服的方式去設定他,而 SECRET_KEY 是這個 app 的 secret key,會用來幫忙處理一些 session 的東西,像是此處的 flash、登入的 session、未來會提到的 csrf_token 等等都會用到它,而有了它也可以反推前述的東西,因此不能外流,此處只是隨便亂打,而且在開發模式,所以是沒有關係的。

如果你在思考這個 flash 的東西到底去哪裡了,可以看看 session,但一樣要記得先 import (from flask import session)。應該可以看到類似 <SecureCookieSession {'_flashes': [('meow', 'meow')]}> 的內容。這個訊息還有一個特性是,在被前端使用過之後就會自動從 session 中消失,所以同樣的訊息不會一直卡著。

接下來看到 index.html,他在最一開始就 import,但是這並不是 python 的 import,他 import 的是一個 jinja 的檔案,而這個檔案在下方,我們等等就會看到。在 body 裡面,我們呼叫剛剛引入的 display_msg,並將 get_flashed_messages(with_categories=True) 作為其引數。這個函式就是用來抓 session 裡面有哪些 flash 的訊息,後面的 with_categories 是說明要不要加上 category,預設是 False。他的表示方式是一個 list,以此處為例,有 with_categories=True 的結果會是 [("cat", "meow")]、沒有打或是設為 False 會得到 ["meow"]

最後看到 msg.jinja,他就像是一個 module,裡面有一個函式可以給別人引入。首先,就如同 if 和 for,他也需要 endmacro,接下來我們可以看到他在 jinja 裡面跑著非常接近 python 的語法,也可以發現剛剛設定的 category 被當成 class 來用。

set & with

在前面講存取變數的時候,可能有人會好奇我們該如何設變數,因為之後用不太到,所以沒有放在前面講,我自己覺得不太會用到的原因是因為我們會用到的變數都已經在 render_template 中設定好了,剩下可能會用到的變數大概也只是 for 的時候會用到的區域變數,所以我個人覺得這個部分不太會使用,等等也不會放上範例。但我自己也真的有用過,像是想要把一些設定檔交給 jinja 去對照,那就會先把設定檔 import 進模板 (如何 import 等等會提到),然後給他一個變數名稱作為簡寫之類的。

標題寫著 set & with,代表這裡有兩種設定變數的方式,差異只在是全域變數還是區域變數而已。我們分開來說。

  • set 的用法是 {% set var=value %},非常簡單。
  • with 就比較麻煩一點,他建立了一個 scope,也就是說他創造的是區域變數。他的用法是 {% with var=value %},但在這個 scope 結束的時候要 {% endwith %},和前面提到的類似。

import

雖然我們是在 python 裡面用 jinja,但是在 jinja 中我們無法使用 python 的函式庫。如果真的要使用的話,我們需要一點小技巧。但我自己是認為,能不要用就不要用。

python 裡面有一個套件叫做 importlib,我們可以在 render_template 後面加上 IMPORT=importlib.import_module (import 在 jinja 裡面是保留字,所以不能用),然後在模板裡面就可以 set var=IMPORT('module'),照著正常該怎麼用就可以了。

References

Configuration Handling - SECRET_KEY
How does the 'with' statement work in Flask (Jinja2)?
Whare are the difference between set and with in jinja
Import a Python module into a Jinja template?


上一篇
Day 6 jinja (1)
下一篇
Day 8 jinja (3)
系列文
Flask30

尚未有邦友留言

立即登入留言