昨天利用內容傳遞網路的資源,成功的做出了一份漂亮的網頁,是不是對網頁設計燃起了一絲興趣呢?還記得我們昨天做的網頁安插了一堆空的連結吧,就是那些href
後面應該加上連結網址(url)結果卻只看到href="#"
,光是瀏覽列大概就有 8 個了。沒錯,今天我們就是要來做更多網頁,並把連結慢慢補完(!?),就從"從頭開始"這個分頁開始吧。
首先,我想要保留主頁(home.html)的<head>
裡面那些設定,再來,我也希望每一個分頁都有相同的導覽列<nav>
,和最底下的<footer>
。那要更改的根本只剩下中間內容的部分而已嘛。也是啊,這樣網頁才有一致性。那麼就來動手囉:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
以下略
不會吧,真的要這樣一個一個打,我們昨天都做的那麼辛苦了,今天為了一個類似的分頁,居然要從頭開始?沒事的沒事的,有一個東西叫做複製貼上,我們可以用複製貼上快速產生一個新的檔案,然後把想保留的地方保留下來,想更動的地方稍做更動就好。是的,這當然是個方法,但是當網頁數量一多,或是網頁越來越複雜,有時我們會連要更動的地方在哪都要在 HTML 5 程式碼堆裡找好久才找得到,還會怕沒看清楚一不小心刪錯程式碼。難道沒有更好的方法嗎?有的,這就是為什麼我們要學 flask 的原因。
開發 flask 的團隊同時也開發了一個與它息息相關的套件 Jinja2。Jinja2 這個套件強大的地方在於,它除了可以幫我們製作網頁模板,讓許許多多的分頁都能套用同一套模板外,更厲害的,還可以叫 Python 幫我們寫 HTML 5 的程式碼。嚇到了吧。不囉嗦,馬上就來看看怎麼做。
要用 Jinja2 做網頁模板,第一件事是找出我們要做的網頁裡共通的部分。以我們昨天設計的網頁為例,在<head>
當中相關的設定我希望能套用到同一系列的網頁上,唯一可能會更改的是<title>
網頁標題的部分。因此做為模板的<head>
會變成這樣:
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 提供 ico 檔案 -->
<link rel="icon" href="{{ url_for('static', filename='img/alpaca_logo.ico') }}">
<!-- 引入 cdn CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans+TC&display=swap">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.css">
<!-- 引入我們自己的 CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}">
<!-- 直接定義 CSS -->
<style> body {font-family: 'Noto Sans TC', sans-serif;}</style>
<!-- 可供更改的部分 -->
<title>{% block title %}{% endblock %}</title>
</head>
上面程式碼最後面一段,看到<title>
標籤當中,出現了神祕的{% block title %}{% endblock %}
,你可以想像成在模板中,我們先在這個位置挖了個洞,預留一個空位,等之後套用這個模板的網頁就可以填上。這就是 Jinja2 運作的方式。
接著來看看<body>
:
<body>
<!-- 希望能固定出現在每個網頁的 nav -->
<nav>
中間略
</nav>
<main>
<!-- 可供更改的部分 -->
{% block main %}{% endblock %}
</main>
<!-- 希望能固定出現在每個網頁的 footer -->
<footer>
中間略
</footer>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<!-- 可供更改的部分 -->
{% block script %}{% endblock %}
</body>
在<body>
當中,我們希望導覽列能夠固定出現在這一系列的網頁上,因此在模板中要認真的把<nav>
當中所有的程式碼都打出來。接著,從<div class="jumbotron">
到<div class="container">
這些則是隨著不同頁面而更改的內容,因此不需要放在模板裡。然而,要在模板中預留空位,讓將來我們套用了模板之後還可以寫入<div class="jumbotron">
、<div class="container">
等等的。怎麼做呢?當然就是加上一段{% block main %}{% endblock %}
最後,也許在不同的頁面上,需要引入不同的 JavaScript,因此也預留一個位置給 Javascript:{% block script %}{% endblock %}
。這樣就做好我們的模板了,先把這麼模板儲存為base.html
然後放進資料夾 template 吧:
D:\alpaca_fighting>tree /F
Folder PATH listing
Volume serial number is 9C33-6XDD
D:.
│ Procfile
│ requirements.txt
│ runtime.txt
│ config.ini
│ clock.py
│ app_day_22.py
│
├───templates
│ base.html
│ home.html
│
├───custom_models
│ PhoebeTalks.py
│ utils.py
│ CallDatabase.py
│ PhoebeFlex.py
│
└───static
├───img
│ alpaca_logo.ico
│
└───css
custom.css
這樣一來我們就有了模板base.html
。現在讓我們回過頭來試著修改一下home.html
,看看套用了base.html
之後,homt.html
的程式碼可以寫得多簡單?
<!-- 宣告我們要套用模板 -->
{% extends "base.html" %}
{% block title %}賴田捕手{% endblock %}
{% block main %}
<div class="jumbotron">
中間略
</div>
<div class="container">
<div class="row desc">
<div class="col">
<h2><i class="fas fa-kiwi-bird"></i> 輕鬆</h2>
<p>中間略</p>
</div>
<div class="col">
<h2><i class="fas fa-smile-wink"></i> 愉悅</h2>
<p>中間略</p>
</div>
<div class="col">
<h2><i class="fas fa-leaf"></i> 無害</h2>
<p>中間略</p>
</div>
</div>
</div>
{% endblock %}
讓我來說明一下這是怎麼運作的。開頭我們用{% extends "base.html" %}
宣告我們要偷懶了,要套用已經寫好而且放在 template 資料夾裡面的base.html
了。接著,還記得我們在base.html
模板裡挖了 3 個洞嗎?第一個洞{% block title %}{% endblock %}
,這回我們用{% block title %}賴田捕手{% endblock %}
填滿了。而這個洞,我們剛好把它挖在<title>
的標籤裡,因此經過 Jinja2 的詮釋,瀏覽器就知道我們要寫的其實是<title>賴田捕手</title>
。有點概念了嗎,很好。那麼第二個洞是{% main block %}{% endblock %}
,這回我們用一長串的內容去填滿,而這一長串的內容,會被放在模板base.html
相對應的洞裡,並交給瀏覽器。最後一個洞{% block script %}{% endblock %}
,在這次設計的頁面裡,我們並沒有想要加入的程式碼,因此就先空著不動它,這樣就完成了。Python 檔案裡的路由不需要更動一樣是:
@app.route("/")
def home():
return render_template("home.html")
這樣就成了,試著把它推向 Heroku 看看成果吧,套用模板base.html
的結果應該跟昨天把所有 HTML 5 程式碼都寫在home.html
裡是一模一樣的。
圖一、套用模板base.html
之後做出來的網站。紅色的字為我們在模板中挖的洞,讓套用該模板的home.html
可以填入不同的內容。
既然學會了 Jinja2 的網頁模板,不如打鐵趁熱,讓我們再做一份網頁吧?
{% extends "base.html" %}
{% block title %}賴田捕手:重頭開始{% endblock %}
{% block main %}
<div class="container welcome">
<div class="row">
<h1><i class="fas fa-baseball-ball"></i> 賴田捕手</h1>
<p> 如果有人真的有興趣聽的話,那麼第一件事情想必是關於我是不是個魯蛇阿宅,不然就是我的身高體重,我平常都吃些什麼,或是家裡眷養了幾隻草尼馬等等不登大雅之堂的問題。不過呢,我並不打算說這些。事實是,我對於討論這些東西一點興趣也沒有。首先得知道的是,草尼馬們並不喜歡被人說三道四的。他們極度纖細敏感,非常重視個人隱私,重視到即使你直直瞪進他們的眼睛,你還是猜不透他們在想些什麼。我並不是在說他們城府很深什麼的,而是因為如果我還不打算進入正題的話,字數限制就要到了。因此,我只準備說一些這半年多來,我從Python、到LINE BOT、到heroku、到資料視覺化,搖搖晃晃跌跌撞撞而又漫無目的的心路歷程。這一路過來,我接受了很多人的幫助,認識或不認識,我都點滴在心。想利用這次鐵人賽的機會,把做一隻LINE BOT的程序有系統的記錄下來,將手頭上好不容易掌握住的東西梳理一遍,也算是權充感謝。關於如何解釋這隻LINE BOT的腳色定位,想了一想,覺得還挺像是個人秘書的。從收集信息,數據處理,到資料視覺化。因此這個鐵人賽的主題,即是從LINE BOT到資料視覺化的30天:賴田捕手。</p>
</div>
</div>
{% endblock %}
{% block script %}
<script>
alert("從頭開始其是套用模板做出來的!");
</script>
{% endblock %}
一樣,一開始我們用{% extends "base.html" %}
來宣告我們想要套用位在 template 資料夾裡的模板base.html
。接著分別在{% block title %}{% endblock %}
、{% block main %}{% endblock %}
、{% block script %}{% endblock %}
三個挖好的洞中加入新的內容。因為我們的模板base.html
中已經引用過 Font Awesome 的內容傳遞網路資源了,因此在這段程式碼中,我可以直接利用 Font Awesome 提供的元素<i class="fas fa-baseball-ball"></i>
而不需要再做一次引用的宣告。我把這份檔案存成from_start.html
,並在 Python 檔案中加入一個路由把from_start.html
放上去:
@app.route("/from_start")
def from_start():
return render_template("from_start.html")
最後記得在base.html
中放入連結的位置。以我而言,我想要放在導覽列中「從頭開始」的連結中。
<li class="nav-item">
<a class="nav-link" href="#">從頭開始</a>
</li>
好了,把它們推上 Heroku 上試試看吧!
圖二、利用 Jinja2 套用模板base.html
做出的另一個網頁
太棒了,用模板又快速做出一份網頁了!
今天我們介紹了如何用 Jinja2 挖洞來製作網頁模板,以及如何套用 Jinja2 製作出來的網頁模板並把需要的內容放入洞中,相關的程式碼我已經放到 Github 上面了,有興趣的人可以到這邊來看看。今天有提到,Jinja2 的強大之處是,它甚至可以幫我們寫 HTML 5 的程式碼。怎麼辦到的呢?我明天會花一個篇幅的內容好好的來介紹一下。最後,今天相關的內容我是根據以下的教學寫成的,有興趣的也可以過去瞧瞧。謝謝大家!
➀ shaoeChen Flask Jinja 樣版繼承
➁ Flask pythonise Jinja 樣版繼承
註:對於此系列文有興趣的讀者,歡迎參考由此系列文擴編成書的 LINE Bot by Python,以及最新的系列文《賴田捕手:追加篇》
第 31 天 初始化 LINE BOT on Heroku
第 32 天 快速回覆 QuickReply 介紹
第 33 天 妥善運用 Heroku APP 暫存空間
第 34 天 妥善運用 LINE Notify 免費推播
第 35 天 製造 Deploy to Heroku 按鈕