Python Falcon - Jinja2 模板



Falcon 庫主要用於構建 API 和微服務。因此,預設情況下,Falcon 響應器返回 JSON 響應。但是,如果內容型別更改為 **falcon.MEDIA_HTML**,則可以渲染 HTML 輸出。

使用可變資料渲染 HTML 內容非常繁瑣。為此,使用 Web 模板庫。許多 Python Web 框架都捆綁了特定的模板庫。但 Falcon 作為一個極簡主義的微框架,並沒有預捆綁任何一個。

**Jinja2** 是許多 Python 框架使用的最流行的模板庫之一。在本節中,我們將瞭解如何在 Falcon 應用程式中使用 Jinja2。Jinja2 是一種快速且對設計人員友好的模板語言,易於配置和除錯。它的沙箱環境使其易於防止執行不受信任的程式碼,禁止潛在的不安全資料,並防止跨站點指令碼攻擊(稱為 **XSS 攻擊**)。

**Jinja2** 的另一個非常強大的功能是 **模板繼承**,您可以在其中定義一個具有通用設計特徵的基本模板,子模板可以覆蓋該模板。

首先,使用 PIP 工具在當前 Python 環境中安裝 **Jinja2**。

pip3 install jinja2

Hello World 模板

**Jinja2** 模組定義了一個 Template 類。透過讀取包含 HTML 指令碼(帶有 .html 副檔名)的檔案內容來獲得 Template 物件。透過呼叫此 Template 物件的 **render()** 方法,可以將 HTML 響應渲染到客戶端瀏覽器。Response 物件的 **content_type** 屬性必須設定為 **falcon.MEDIA_HTML**。

讓我們將以下 HTML 指令碼另存為應用程式資料夾中的 **hello.py**。

<html>
   <body>
      <h2>Hello World</h2>
   </body>
</html>

示例

下面資源類中的 **on_get()** 響應器讀取此檔案並將其渲染為 HTML 響應。

import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
   async def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type = 'text/html'
      fp=open("hello.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render()
app = falcon.asgi.App()
hello = HelloResource()
app.add_route('/hello', hello)
if __name__ == "__main__":
   uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)

輸出

執行上面的 Python 程式碼,並在瀏覽器中訪問 **https://:8000/hello** 連結。

Jinja2

模板變數

**Jinja2** 是一個伺服器端模板庫。網頁被構建為一個模板,透過在 HTML 指令碼內的適當分隔符內將 Jinja2 模板語言的各種元素作為佔位符。模板引擎讀取 HTML 指令碼,在伺服器上用上下文資料替換佔位符,重新組裝 HTML,並將其渲染到客戶端。

**Template.render()** 函式有一個可選的上下文字典引數。此字典的關鍵屬性成為模板變數。這有助於在網頁中渲染響應器傳遞的資料。

示例

在以下示例中,路由 ** /hello/nm** 與資源物件註冊,其中 nm 是路徑引數。**on_get()** 響應器將其作為上下文傳遞給從網頁獲得的模板物件。

import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
   async def on_get(self, req, resp, nm):
      resp.status = falcon.HTTP_200
      resp.content_type = 'text/html'
      fp=open("hello.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({'name':nm})
app = falcon.asgi.App()
hello = HelloResource()
app.add_route('/hello/{nm}', hello)
if __name__ == "__main__":
   uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)

**hello.html** 在模板變數名稱中讀取路徑引數。它充當 HTML 指令碼中的佔位符。它放在 **{{** 和 **}}** 符號中,以便其值顯示為 HTML 響應。

<html>
   <body>
      <h2>Hello {{ name }}</h2>
   </body>
</html>

輸出

執行 Python 程式碼並輸入 **https://:8000/hello/Priya** 作為 URL。瀏覽器顯示以下輸出 -

Jinja2 Hello

Jinja2 模板中的迴圈

如果響應器傳遞任何 Python 可迭代物件,例如列表、元組或字典,則可以使用其迴圈構造語法在 Jinja2 模板中遍歷其元素。

{% for item in collection %}
HTML block
{% endfor %}

在以下示例中,**on_get()** 響應器將 students 物件(它是 **dict** 物件列表)傳送到模板 **list.html**。它依次遍歷資料並將其渲染為 HTML 表格。

import falcon
import json
from waitress import serve
from jinja2 import Template
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_HTML
      fp=open("list.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({'students':students})

**list.html** 是一個 Jinja2 模板。它接收 students 物件作為字典物件的列表,並將每個鍵的值放在表格的 <td>..<.td> 元素內。

<html>
<body>
<table border=1>
   <thead> <tr>
      <th>Student ID</th> <th>Student Name</th>
      <th>percentage</th>
      <th>Actions</th>
   </tr> </thead>
   <tbody>
   {% for Student in students %}
   <tr> <td>{{ Student.id }}</td> <td>{{ Student.name }}</td>
      <td>{{ Student.percent }}</td>
      <td>
         <a href="#">Edit</a>
         <a href="#">Delete</a>
      </td> </tr>
   {% endfor %}
   </tbody>
</table>
</body>
</html>

在瀏覽器的位址列中訪問 ** /students** 路由。學生列表將在瀏覽器中呈現。

Jinja2 Image

HTML 表單模板

在本節中,我們將瞭解 Falcon 如何從 HTML 表單讀取資料。讓我們將以下 HTML 指令碼另存為 myform.html。我們將使用它來獲取 Template 物件並渲染它。

<html>
<body>
   <form method="POST" action="https://:8000/students">
   <p>Student Id: <input type="text" name="id"/> </p>
   <p>student Name: <input type="text" name="name"/> </p>
   <p>Percentage: <input type="text" name="percent"/> </p>
   <p><input type="submit"> </p>
</body>
</html>

Falcon App 物件在 Hello.py 檔案中宣告,該檔案還具有對映到 ** /adddnew** 路由的資源類。**on_get()** 響應器讀取 **myform.html** 並渲染相同的內容。將顯示 HTML 表單。表單透過 POST 方法提交到 ** /students** 路由。

為了能夠讀取表單資料,必須將 **falcon.RequestOptions** 類的 **auto_parse_form_urlencoded** 屬性設定為 True。

app = falcon.App()
app.req_options.auto_parse_form_urlencoded = True

這裡,我們還從 **student.py** 匯入 **StudentResource** 類。**on_get()** 響應器呈現學生列表。

當用戶填寫並提交表單時,將呼叫 **on_post()** 響應器。此方法在 **req.params** 屬性中收集表單資料,這只是一個表單元素及其值的字典。然後追加 **students** 字典。

def on_post(self, req, resp):
   student=req.params
   students.append(student)

**hello.py** 的完整程式碼如下:

import falcon
import json
from waitress import serve
from jinja2 import Template
from student import StudentResource
class MyResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type = 'text/html'
      fp=open("myform.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render()
app = falcon.App()
app.req_options.auto_parse_form_urlencoded = True
form = MyResource()
app.add_route('/addnew', form)
app.add_route("/students", StudentResource())
if __name__ == '__main__':
   serve(app, host='0.0.0.0', port=8000)

包含 **StudentResource** 類以及 **on_get()** 和 **on_post()** 響應器的 student.py 如下:

import falcon
import json
from waitress import serve
from jinja2 import Template
students = [
   {"id": 1, "name": "Ravi", "percent": 75.50},
   {"id": 2, "name": "Mona", "percent": 80.00},
   {"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_HTML
      fp=open("list.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render({'students':students})

   def on_post(self, req, resp):
      student = req.params
      students.append(student)
      resp.text = "Student added successfully."
      resp.status = falcon.HTTP_OK
      resp.content_type = falcon.MEDIA_JSON

從命令列執行 **hello.py**。透過輸入 **http://locLhost:8000/addnew** 在瀏覽器中開啟 HTML 表單。

Jinja2 Host

**students** 資料庫字典將被追加。訪問 ** /students** 路由。您會發現添加了一行新資料。

Jinja2 Example

多部分表單

為了讓使用者從本地檔案系統中選擇檔案,HTML 表單的 **enctype** 屬性必須設定為 multipart/form-data。Falcon 使用 **MultipartFormHandler** 處理 multipart/form-data 媒體型別,允許它遍歷表單中的主體部分。

**BodyPart** 類具有以下屬性:

  • **stream** - 僅適用於當前主體部分的流包裝器

  • **data** - 主體部分內容位元組

  • **content_type** 如果未指定,則預設為 text/plain,根據 RFC

  • **text** - 當前主體部分解碼為文字字串(僅在型別為 text/plain 時提供,否則為 None)

  • **media** - 透過媒體處理程式自動解析,方式與 req.media 相同

  • **name, filename** - 來自 Content-Disposition 標頭的相關部分

  • **secure_filename** - 可以安全地用於伺服器檔案系統的經過清理的檔名。

以下 HTML 指令碼(**index.html**)是一個多部分表單。

<html>
   <body>
      <form action="https://:8000/hello" method="POST" enctype="multipart/form-data">
         <h3>Enter User name</h3>
         <p><input type='text' name='name'/></p>
         <h3>Enter address</h3>
         <p><input type='text' name='addr'/></p>
         <p><input type="file" name="file" /></p>
         <p><input type='submit' value='submit'/></p>
      </form>
   </body>
</html>

此表單由下面程式碼中 **HelloResource** 類的 **on_get()** 響應器呈現。表單資料提交到 **on_post()** 方法,該方法遍歷各部分併發送表單資料的 JSON 響應。

import waitress
import falcon
import json
from jinja2 import Template
class HelloResource:
   def on_get(self, req, resp):
      resp.status = falcon.HTTP_200
      resp.content_type = 'text/html'
      fp=open("index.html","r")
      tempobj=Template(fp.read())
      resp.body=tempobj.render()

   def on_post(self, req, resp):
      result=[]
      for part in req.media:
         data={"name" :part.name,
            "content type":part.content_type,
            "value":part.text, "file":part.filename}
         result.append(data)
         resp.text = json.dumps(result)
         resp.status = falcon.HTTP_OK
         resp.content_type = falcon.MEDIA_JSON
app = falcon.App()
hello = HelloResource()
app.add_route('/hello', hello)
if __name__ == '__main__':
   waitress.serve(app, host='0.0.0.0', port=8000)

執行上述程式並訪問 **https://:8000/hello** 連結以渲染表單,如下所示:

Jinja2 User

填寫資料並提交表單後,JSON 響應將在瀏覽器中呈現,如下所示:

[
   {
      "name": "name",
      "content type": "text/plain",
      "value": "SuyashKumar Khanna",
      "file": null
   },
   {
      "name": "addr",
      "content type": "text/plain",
      "value": "New Delhi",
      "file": null
   },
   {
      "name": "file",
      "content type": "image/png",
      "value": null,
      "file": "hello.png"
   }
]
廣告

© . All rights reserved.