Python Flask 概念與實作(五) - 渲染模板

前言

網站框架有一個美美的前端是非常重要的一件事,畢竟如果你不是單純提供API服務的話,最後呈現的仍是網站前端頁面。然而如何在Flask中渲染你的前端畫面呢?讓我們繼續看下去…


目前 Python Flask 概念與實作 大致規劃為

  1. 佈置環境
  2. Flask model運用(一)
  3. Web server概念(二)
  4. 專案檔案分佈(三)
  5. 連接資料庫(四)
  6. 渲染模板(五)
  7. 登入功能(六)
  8. Blog功能(七)

在開發過程中,Test(測試)其實也很重要,但礙於缺乏經驗,若之後有望的話再補上(專案打包、網站優化等等亦同)


Bootstrap

有前端開發經驗的人應該對bootstrap不陌生,bootstrap是一個前端框架,裡面有寫好漂漂亮亮的HTML, CSS, JavaScript模板開放給你使用,而flask也有套件可以連接bootstrap。相關使用教學請參閱使用 Flask Bootstrap 渲染你的HTML

Jinja 2

使用 Flask Bootstrap 渲染你的HTML中,我們已經知道如何渲染模板,雖然還沒詳述jinja 2,但基本上與python語法差異不大,主要都使用{}將語法包起來,而瀏覽器會將jinja 2語法轉成html。(當然jinja 2還有很多語法e.g. import等等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{# 註解: 瀏覽器不會編譯這裡 #}

{% set price = 250 %}
<!-- 設price變數為250 -->

<span>啊嬤說高麗菜一斤{{ price}}
{% if price >= 1000 %}
夭壽貴
{% elif price >=50 %}
普通啦
{% else %}
修夠大碗
{% endif %}
</span>
<!-- 啊嬤說高麗菜一斤250普通啦 -->

<span>啊嬤叫你
{% for i in range(5) %}
緊凳來喔~
{% endfor %}
</span>
<!-- 啊嬤叫你 緊凳來喔~ 緊凳來喔~ 緊凳來喔~ 緊凳來喔~ 緊凳來喔~ -->

使用Jina 2繼承模板

前端網站通常都有首頁與很多分頁,正常來說我們希望這些頁面都使用同一個主題,因此會設計一個公版模型。如此一來,可以保持每個分頁一至,並且省下很多時間在複製貼上與減少重複的code。

以甜點訂餐網站Shijuu 西啾來說,大致上有這些頁面:

  • 首頁 index.html
  • 商品頁面 products.html
  • 訂單查詢 order_query.html
  • 關於shijuu about.html

可以看得出來是使用同一個模板,而下面的架構除了<main>標籤以外,幾乎每一個都是相同的
結構大致是這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Shijuu</title>
<!-- CSS Files... -->
</head>
<body>
<header>navbar</header>
<main>
<!-- 許多section標籤... -->
</main>
<footer>頁尾</footer>
</body>

<!-- Java Script Files... -->
</html>

Jija 2有模板繼承功能,我們新增base.html,將上述的html寫入,並新增Jija 2語法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- base.html -->
<!DOCTYPE html>
<html>
<head></head>
<body>
<header>navbar</header>
<main>
{% block container %}
{% end block %}
</main>
<footer>頁尾</footer>
</body>
<!-- Java Script Files... -->
</html>

有了base.html,我們的index.html可以直接從<main>標籤開始撰寫!!

1
2
3
4
5
6
7
8
9
<!-- index.html -->
{# 繼承'base.html' #}
{% extends "base.html" %}

{# 覆蓋block container #}
{% block container %}
<section></section>
<section></section>
{% end block %}

如此一來,每一個.html均變得非常簡潔,且再也不用擔心改code牽一髮而動全身。
base.html越來越臭長,也可以新增navbar.html & footer.html 等,並使用{% include %}標籤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- base.html V2.0 -->
<!DOCTYPE html>
<html>
<head></head>
<body>
<header>
{# 引入'navbar.html' #}
{% include "navbar.html" %}
</header>
<main>
{% block container %}
{% end block %}
</main>
<footer>
{# 引入'footer.html' #}
{% include "footer.html" %}
</footer>
</body>
<!-- Java Script Files... -->
</html>

檔案分佈如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
.
├── run.py
└── app
├── __init__.py
├── routes.py
├── models.py
├── forms.py
├── static
└── templates
├── base.html
├── about.html
└── index.html

Flash

Flask提供Flash這個好用的功能,顧名思義一閃即逝,flask會將警示訊息等加入session的message中。且僅Flash只會觸發一次,常用作警示、提示等用途。Flash的Jinja 2語法通常寫在base.html中,如此一來所有模板也能繼承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- base.html -->

<!-- 略... -->
<main>
<body>
<div class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
<span class="alert-inner--text">{{ message }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>

{% block container%}
{% end block %}
</body>
</main>
1
2
3
4
5
6
7
8
9
10
# routes.py

# 略...
@app.route('/')
def index()
# 登入後, session會存'username'變數
# 若已登入, 則顯示訊息. category會依bootstrap設定顯示不同顏色
if "username" in session:
flash(f"Logged in as {session['username']}", category="success")
return render_template("index.html")

結論

  • {% block %} & {% end block %} 是一個可以隨時替換區塊
  • {% extends %} 可以完整的繼承其他.html
  • {% include %} 可以插入其他.html
  • 使用flash在前端即時顯示訊息