FastAPI 是一個高效能的 Python Web 框架,而 Jinja2 作為其推薦的範本引擎,能有效地簡化網頁應用開發。本文將介紹如何整合 Jinja2 與 FastAPI,並探討範本渲染、變數傳遞、迴圈、條件判斷和靜態資源管理等核心技巧。透過 Jinja2TemplatesTemplateResponse,我們能輕鬆地將 Python 資料動態地嵌入 HTML 範本。同時,文章也涵蓋了路徑引數的運用,以及如何傳遞和處理複雜的資料結構,例如字典。此外,我們將學習如何在 Jinja2 範本中使用條件陳述式和迴圈結構,實作更精細的網頁邏輯控制。最後,文章還將介紹如何有效地管理和使用靜態資源,例如 JavaScript 檔案、圖片和 CSS 樣式表,讓網頁應用更具完整性和互動性。

FastAPI 中的範本引擎應用

HTML 回應與範本引擎簡介

在前面的章節中,我們已經瞭解 HTMLResponseResponse 類別的子類別,主要用於處理 HTML 內容的回應。HTMLResponsecontent 引數接受一個字串(或位元組字串),並需要從 fastapi.responses 模組匯入。此外,我們還需要將其設定為 @app.get() 裝飾器的 response_class 引數值。

使用 HTMLResponse 類別的範例

清單 4-3 展示瞭如何使用 HTMLResponse 類別,並定義了一個路徑引數 name 傳遞給 index() 函式。其目的是從 URL 路徑中解析該引數,以便在瀏覽器中存取 http://localhost:8000/Rahul 時,函式能夠呈現 “Hello Rahul” 的文字。

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/{name}", response_class=HTMLResponse)
async def index(name):
    ret = '''
    <html>
    <body>
    <h2 style="text-align: center;">Hello {}!</h2>
    </body>
    </html>
    '''.format(name)
    return HTMLResponse(content=ret)

程式碼解析:

  • 使用 str 類別的 format() 方法將路徑變數的值插入到字串中的佔位符。
  • 瀏覽器的 HTML 解析引擎負責處理標籤及其屬性,從而顯示如圖 4-1 所示的結果。

範本引擎的工作原理

HTML 是用於構建網頁的語言,本質上是靜態的。雖然我們在前面的範例中透過將 HTML 程式碼的字串表示與變數部分混合來增加了一定的互動性,但這種方法非常繁瑣。想像一下,如果需要有條件地顯示表格資料,將會有多麼困難。

大多數現代網頁應用框架都使用網頁範本系統來實作此目的。範本引擎(或稱範本處理器)接收範本和資料來源,將資料來源中的資料項逐一放入範本中的相應佔位符,從而動態生成多個網頁。圖 4-2 說明瞭範本引擎的工作原理。

範本引擎的組成部分:

  1. 範本引擎:負責處理範本和資料來源。
  2. 資料來源:提供要插入範本的資料,可以是資料函式庫表格、記憶體陣列或 CSV 檔案等。
  3. 網頁範本:包含一個或多個範本語言程式碼區塊的網頁,用於填充動態資料。

在 FastAPI 中使用 Jinja2 範本引擎

FastAPI 並未依賴特定的範本引擎,但推薦使用 jinja2 套件。由於 jinja2 預設未安裝,因此需要使用 pip3 install jinja2 命令進行安裝。

Jinja2 的特點:

  • 快速且輕量:適合用於網頁應用的範本渲染。
  • 沙箱環境:禁止使用潛在的不安全資料,有效防止跨站攻擊。
  • 範本繼承:強大的功能,有助於保持網頁設計的一致性。

建立 “Hello World” 範本

首先,將 HTML 字串儲存為 hello.html(清單 4-4),並放置在與 app.py 指令碼相同的 templates 資料夾中。

<html>
<body>
<h2 style="text-align: center;">Hello World!</h2>
</body>
</html>

使用 Jinja2Templates 設定範本物件

FastAPI 對 jinja2 的支援以 Jinja2Templates 函式的形式提供,該函式定義在 fastapi.templating 模組中。我們需要匯入並使用它來建立範本物件(清單 4-5)。

from fastapi.templating import Jinja2Templates

templates = Jinja2Templates(directory="templates")

使用 TemplateResponse 渲染範本

在操作函式中,需要接收 request 物件引數,並將其作為請求上下文傳遞給範本(清單 4-6)。

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

templates = Jinja2Templates(directory="templates")
app = FastAPI()

@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    return templates.TemplateResponse("hello.html", {"request": request})

資料夾結構:

app/
│ main.py
└───templates/
        hello.html

最終程式碼(清單 4-7):

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

templates = Jinja2Templates(directory="templates")
app = FastAPI()

@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    return templates.TemplateResponse("hello.html", {"request": request})

隨著網頁應用的複雜度不斷增加,使用範本引擎來管理動態內容已成為一種標準做法。FastAPI 與 Jinja2 的結合為開發者提供了一個強大且靈活的工具,用於構建現代網頁應用。未來,我們可以期待更多的功能擴充套件和效能最佳化,以滿足不斷變化的開發需求。

圖表說明:範本引擎工作原理

  graph LR
A[資料來源] --> B[範本引擎]
C[網頁範本] --> B
B --> D[動態網頁]

圖表翻譯: 此圖示展示了範本引擎的工作原理。資料來源和網頁範本被輸入到範本引擎中,經過處理後生成動態網頁。這種機制使得網頁內容能夠根據不同的資料來源動態變化,提高了網頁應用的靈活性和可維護性。

詳細解說:

  1. 資料來源:可以是資料函式庫、記憶體中的資料結構或是外部檔案等。
  2. 網頁範本:定義了網頁的基本結構,並包含用於插入動態內容的佔位符。
  3. 範本引擎:負責將資料來源中的資料填充到網頁範本中,生成最終的動態網頁。
  4. 動態網頁:根據不同的資料來源生成的不同網頁內容,供使用者存取。

這種機制在現代網頁應用中非常常見,能夠有效地分離網頁的結構和內容,提高開發效率和網頁的可維護性。

範本引擎與動態內容渲染

在現代Web開發中,範本引擎扮演著至關重要的角色。它允許開發者將應用程式的邏輯與呈現層分離,從而提高程式碼的可維護性和可讀性。本章節將探討FastAPI框架中範本引擎的使用,特別是Jinja2範本語言的應用。

靜態與動態範本渲染

首先,讓我們從一個簡單的例子開始。考慮以下基本的HTML範本:

<html>
<body>
    <h2 style="text-align: center;">Hello World!</h2>
</body>
</html>

這個範本可以透過FastAPI的TemplateResponse方法渲染:

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    return templates.TemplateResponse("hello.html", {"request": request})

內容解密:

  1. Jinja2Templates類別用於初始化範本引擎,並指定範本檔案所在的目錄。
  2. TemplateResponse方法負責渲染指定的範本檔案,並將必要的上下文資料傳遞給範本。
  3. 在這個例子中,我們傳遞了request物件,這是Jinja2範本引擎所必需的。

路徑引數與範本變數

為了使範本更加動態,我們可以將路徑引數傳遞給範本。修改後的檢視函式如下所示:

@app.get("/{name}", response_class=HTMLResponse)
async def index(request: Request, name: str):
    return templates.TemplateResponse("hello.html", {"request": request, "name": name})

對應的範本檔案hello.html可以修改為:

<html>
<body>
    <h2 style="text-align: center;">Hello {{ name }}!</h2>
</body>
</html>

內容解密:

  1. 在檢視函式中,我們將name引數新增到上下文字典中。
  2. 在範本中,我們使用雙大括號{{ name }}來參照這個變數。
  3. Jinja2範本引擎會在渲染時將name變數替換為實際的值。

傳遞複雜資料結構

除了簡單的變數外,我們還可以將複雜的資料結構(如字典)傳遞給範本。考慮以下例子:

@app.get("/employee/{name}/{salary}", response_class=HTMLResponse)
async def employee(request: Request, name: str, salary: int):
    data = {"name": name, "salary": salary}
    return templates.TemplateResponse("employee.html", {"request": request, "data": data})

對應的employee.html範本:

<html>
<body>
    <h1>Employee Details</h1>
    <h2>Name: {{ data.get('name') }} Salary: {{ data.get('salary') }}</h2>
</body>
</html>

內容解密:

  1. 我們建立了一個包含員薪水訊的字典data
  2. 在範本中,我們使用data.get('key')語法來存取字典中的值。
  3. 這種方法允許我們在範本中安全地存取資料,即使某些鍵不存在也不會導致錯誤。

條件陳述式在範本中的應用

Jinja2範本語言支援條件陳述式,使我們能夠根據特定的條件渲染不同的內容。以下是一個例子:

<html>
<body>
    <h1>Employee Details</h1>
    <h2>Name: {{ data.get('name') }} </h2>
    <h2>Salary: {{ data.get('salary') }}</h2>
    {% if data.get('salary') >= 25000 %}
        <h2>Income Tax : {{ data.get('salary') * 0.10 }}</h2>
    {% else %}
        <h2>Income Tax : Not applicable</h2>
    {% endif %}
</body>
</html>

內容解密:

  1. 我們使用{% if %}標籤來開始條件陳述式。
  2. 在條件表示式中,我們可以直接存取範本變數並進行比較操作。
  3. {% else %}子句用於處理條件不成立的情況。
  4. {% endif %}標籤標誌著條件陳述式的結束。
  5. Jinja2的條件陳述式語法與Python非常相似,但需要注意的是它使用特定的標籤來分隔邏輯和呈現層。

在範本中使用迴圈與靜態資源

範本中的迴圈結構

在 Jinja2 範本中,可以使用迴圈結構來遍歷序列物件,如列表、元組或字串。透過 forendfor 關鍵字,可以實作迴圈功能。

基本語法

{% for item in sequence %}
    <!-- HTML 區塊 -->
{% endfor %}

實際範例

假設我們有一個列表 langs,其中包含多種程式語言的名稱。在 main.py 中,我們將這個列表傳遞給範本:

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/profile/", response_class=HTMLResponse)
async def info(request: Request):
    data = {"name": "Ronie", "langs": ["Python", "Java", "PHP", "Swift", "Ruby"]}
    return templates.TemplateResponse("profile.html", {"request": request, "data": data})

profile.html 範本中,我們使用 for 迴圈來遍歷 langs 列表,並將每個語言名稱渲染為 HTML 的無序列表元素:

<html>
<body>
    <h2>名稱:{{ data.get('name') }} </h2>
    <h3>程式語言能力</h3>
    <ul>
        {% for lang in data.get('langs') %}
            <b><li> {{ lang }}</li></b>
        {% endfor %}
    </ul>
</body>
</html>

輸出結果

當我們存取 http://localhost:8000/profile/ 時,瀏覽器將顯示如下內容:

名稱:Ronie
程式語言能力
• Python
• Java
• PHP
• Swift
• Ruby

提供靜態資源

Jinja2 範本引擎能夠動態替換變數資料以生成網頁。然而,為了統一顯示內容,HTML 通常會使用樣式表、圖片和 JavaScript 檔案。這些資源不隨變數資料而改變,因此被稱為網站的靜態資源。

組態靜態檔案目錄

在 FastAPI 中,我們需要將靜態檔案放在名為 static 的資料夾中。該資料夾應位於應用程式碼指令碼和 templates 資料夾所在的同一目錄下。

from fastapi.staticfiles import StaticFiles

app.mount("/static", StaticFiles(directory="static"), name="static")

在範本中使用靜態資源

要使用靜態資源,我們可以使用 Jinja2 的 url_for() 函式來取得資源的路徑。

使用 JavaScript 檔案
<script src="{{ url_for('static', path='myscript.js') }}"></script>

main.py 中定義對應的路由:

@app.get("/testjs/{name}", response_class=HTMLResponse)
async def jsdemo(request: Request, name: str):
    data = {"name": name}
    return templates.TemplateResponse("static-js.html", {"request": request, "data": data})

static-js.html 範本中,我們可以使用 JavaScript 檔案中的函式:

<!DOCTYPE html>
<html>
<head>
    <title>我的網站</title>
    <script src="{{ url_for('static', path='myscript.js') }}"></script>
</head>
<body>
    <h2>在範本中使用 JavaScript</h2>
    <h3> 歡迎 {{ data.get('name') }}</h3>
    <button onclick="myFunction()">提交</button>
    <p id="response"></p>
</body>
</html>

myscript.js 檔案中定義 myFunction()

function myFunction() {
    let text;
    if (confirm("您要繼續嗎?\n選擇 確定/取消") == true) {
        text = "您按下了確定!";
    } else {
        text = "您按下了取消";
    }
    document.getElementById("response").innerHTML = text;
}

使用靜態圖片

要在網頁中顯示圖片,我們可以使用 <img> 標籤,並透過 url_for() 函式取得圖片路徑:

<img src="{{ url_for('static', path='fa-logo.png') }}" >

main.py 中定義對應的路由:

@app.get("/img/", response_class=HTMLResponse)
async def showimg(request: Request):
    return templates.TemplateResponse("static-img.html", {"request": request})

static-img.html 範本中顯示圖片:

<!DOCTYPE html>
<html>
<head>
    <title>我的網站</title>
</head>
<body>
    <h2 style="text-align: center;">範本中的靜態圖片</h2>
    <img src="{{ url_for('static', path='fa-logo.png') }}" >
</body>
</html>

隨著網頁技術的不斷發展,範本引擎和靜態資源管理將繼續扮演重要的角色。未來,我們可以期待更多高效的範本引擎和靜態資源管理方案,以滿足日益增長的網頁應用需求。

範本渲染流程圖示

  graph LR;
    A[請求路由] --> B[範本引擎渲染];
    B --> C[遍歷列表資料];
    C --> D[渲染 HTML 元素];
    D --> E[傳回渲染結果];
    E --> F[瀏覽器顯示結果];

圖表翻譯:

此圖示展示了範本渲染的基本流程。首先,使用者發起請求到指定的路由。接著,範本引擎根據提供的資料進行渲染。在渲染過程中,如果遇到列表資料,則會遍歷該列表並生成對應的 HTML 元素。最終,渲染完成的結果傳回給使用者,並在瀏覽器中顯示出來。