模板

Flask 利用 Jinja2 作為其模板引擎。您顯然可以自由使用不同的模板引擎,但您仍然必須安裝 Jinja2 才能運行 Flask 本身。此要求是啟用豐富擴充功能所必需的。擴充功能可以依賴 Jinja2 的存在。

本節僅簡要介紹 Jinja2 如何整合到 Flask 中。如果您想了解模板引擎語法本身的資訊,請前往官方 Jinja2 模板文件 以獲取更多資訊。

Jinja 設定

除非另行自訂,否則 Jinja2 由 Flask 配置如下

  • 當使用 render_template() 時,對於所有以 .html.htm.xml.xhtml 以及 .svg 結尾的模板,會啟用自動跳脫。

  • 當使用 render_template_string() 時,對於所有字串,會啟用自動跳脫。

  • 模板可以使用 {% autoescape %} 標籤選擇啟用/停用自動跳脫。

  • 除了預設存在的值之外,Flask 還會在 Jinja2 上下文中插入一些全域函數和輔助程式。

標準上下文

以下全域變數預設在 Jinja2 模板中可用

config

目前的組態物件 (flask.Flask.config)

變更日誌

在版本 0.10 中變更:現在始終可用,即使在匯入的模板中也是如此。

在版本 0.6 中加入。

request

目前的請求物件 (flask.request)。如果模板在沒有活動請求上下文的情況下呈現,則此變數不可用。

session

目前的會話物件 (flask.session)。如果模板在沒有活動請求上下文的情況下呈現,則此變數不可用。

g

請求綁定的全域變數物件 (flask.g)。如果模板在沒有活動請求上下文的情況下呈現,則此變數不可用。

url_for()

flask.url_for() 函數。

get_flashed_messages()

flask.get_flashed_messages() 函數。

Jinja 上下文行為

這些變數會新增到變數的上下文中,它們不是全域變數。不同之處在於,預設情況下,這些變數不會顯示在匯入模板的上下文中。這部分是由於效能考量,部分是為了保持明確性。

這對您來說意味著什麼?如果您有一個想要匯入的巨集,需要存取請求物件,您有兩種可能性

  1. 您可以將請求或您感興趣的請求物件屬性顯式地作為參數傳遞給巨集。

  2. 您可以使用「上下文」匯入巨集。

使用上下文匯入看起來像這樣

{% from '_helpers.html' import my_macro with context %}

控制自動跳脫

自動跳脫是指自動為您跳脫特殊字元的概念。HTML(或 XML,以及 XHTML)意義上的特殊字元是 &><" 以及 '。因為這些字元在文件中本身就帶有特定含義,如果您想將它們用於文字,則必須將它們替換為所謂的「實體」。不這樣做不僅會因無法在文字中使用這些字元而導致使用者沮喪,還可能導致安全問題。(請參閱 跨站腳本攻擊 (XSS)

但是,有時您需要停用模板中的自動跳脫。如果您想將 HTML 顯式地注入到頁面中,例如它們來自產生安全 HTML 的系統(如 Markdown 到 HTML 轉換器),則可能會發生這種情況。

有三種方法可以實現這一點

  • 在 Python 程式碼中,在將 HTML 字串傳遞到模板之前,將其包裝在 Markup 物件中。這通常是建議的方法。

  • 在模板內部,使用 |safe 篩選器將字串顯式標記為安全 HTML ({{ myvariable|safe }})

  • 暫時完全停用自動跳脫系統。

若要在模板中停用自動跳脫系統,您可以使用 {% autoescape %} 區塊

{% autoescape false %}
    <p>autoescaping is disabled here
    <p>{{ will_not_be_escaped }}
{% endautoescape %}

每當您執行此操作時,請務必謹慎處理您在此區塊中使用的變數。

註冊篩選器

如果您想在 Jinja2 中註冊自己的篩選器,您有兩種方法可以做到這一點。您可以手動將它們放入應用程式的 jinja_env 中,或使用 template_filter() 裝飾器。

以下兩個範例的工作方式相同,並且都反轉物件

@app.template_filter('reverse')
def reverse_filter(s):
    return s[::-1]

def reverse_filter(s):
    return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter

在使用裝飾器的情況下,如果您想使用函數名稱作為篩選器的名稱,則參數是選填的。註冊後,您可以在模板中使用篩選器,方式與 Jinja2 的內建篩選器相同,例如,如果您在上下文中有名為 mylist 的 Python 列表

{% for x in mylist | reverse %}
{% endfor %}

上下文處理器

為了將新變數自動注入到模板的上下文中,Flask 中存在上下文處理器。上下文處理器在模板呈現之前運行,並具有將新值注入到模板上下文中的能力。上下文處理器是一個返回字典的函數。然後,此字典的鍵和值與應用程式中所有模板的模板上下文合併

@app.context_processor
def inject_user():
    return dict(user=g.user)

上面的上下文處理器使模板中名為 user 的變數可用,其值為 g.user。此範例不是很有趣,因為 g 無論如何都可以在模板中使用,但它給出了這個工作原理的想法。

變數不限於值;上下文處理器還可以使函數在模板中可用(因為 Python 允許傳遞函數)

@app.context_processor
def utility_processor():
    def format_price(amount, currency="€"):
        return f"{amount:.2f}{currency}"
    return dict(format_price=format_price)

上面的上下文處理器使 format_price 函數可用於所有模板

{{ format_price(0.33) }}

您也可以將 format_price 建置為模板篩選器(請參閱 註冊篩選器),但這示範了如何在上下文處理器中傳遞函數。

串流

將整個模板作為一個完整的字串呈現可能不是很有用,而是將其呈現為串流,產生較小的增量字串。這可以用於將 HTML 分塊串流以加速初始頁面載入,或在呈現非常大的模板時節省記憶體。

Jinja2 模板引擎支援逐片呈現模板,返回字串的迭代器。Flask 提供了 stream_template()stream_template_string() 函數,使這更容易使用。

from flask import stream_template

@app.get("/timeline")
def timeline():
    return stream_template("timeline.html")

如果請求處於活動狀態,這些函數會自動應用 stream_with_context() 包裝器,以便它在模板中保持可用。