2023年9月30日 星期六

Python 學習筆記 : Django 4 用法整理 (二) 網頁模板

這周都在複習 Django, 我把心得整理在下面的筆記裡 :


今年中秋連假不烤肉, 窩在家繼續複習 Django 整理模板用法. 

在上一篇測試中均使用 django.http.HttpResponse() 函式直接將 HTML 碼回應給用戶端, 但這不僅麻煩, 且不利於前後端分工. Django 內建的模板引擎可解決此問題, 前端設計師負責網頁模板設計, 後端工程師則可利用 django.shortcut.render() 函式將變數傳遞給指定的模板網頁顯示, 架構如下 : 




網頁模板需要放在手動建立的特定資料夾下 (通常取名為 templates), 其位置有二 :
  • 在上層專案目錄下 (專案模板) :
    整個網站與旗下的 App 均可使用, 適用於單一 App 網站  (App 可移植性低).
  • 在 App 目錄下 (App 模板) : 
    每一個 App 都可以有自己的模板資料夾 (App 可移植性高). 
當然也可以兩者均有, 專案 templates 用來存放網站首頁相關的模板; 而 App 的 templates 則用來存放該 App 相關的網頁模板. 在前一篇的測試最後, 得到一個可移植性佳的單一 App 網站, 其架構如下圖所示 :


mysite                                      (上層專案目錄)
     |____ manage.py                (管理程式)
     |____ mysite                        (下層專案目錄)
     |             |____ __init__.py   (形成套件)
     |             |____ settings.py    (專案設定檔)
     |             |____ urls.py           (專案路由模組, 自動建立)
     |             |____ views.py        (專案視圖模組, 手工添加)
     |             |____ wsgi.py         (伺服器佈署設定檔)
     |             |____ asgi.py         (伺服器佈署設定檔, 非同步)
     |____ myapp1                      (應用程式目錄)
                   |____ migrations     (資料庫同步)
                   |____ __init__.py    (形成套件)
                   |____ admin.py       (註冊資料模型)
                   |____ apps.py         (App 設定檔)
                   |____ models.py     (資料模型定義)
                   |____ tests.py          (測試用程式)
                   |____ views.py        (App 視圖模組, 自動建立)
                   |____ urls.py           (App 路由模組, 手工添加)


可見與網站首頁相關的回應由專案的控制器 (mysite 下的 urls.py 與 views.y) 負責, 而與 App 相關的回應則由 App 的控制器 (myapp1 下的 urls.py 與 views.y) 負責. 現在於上層 mysite 與 myapp1 下各自建立一個 templates 資料夾, 架構如下 : 

mysite                                       (上層專案目錄)
     |____ manage.py                 (管理程式)
     |____ mysite                         (下層專案目錄)
     |             |____ __init__.py    (形成套件)
     |             |____ settings.py    (專案設定檔)
     |             |____ urls.py           (專案路由模組, 自動建立)
     |             |____ views.py        (專案視圖模組, 手工添加)
     |             |____ wsgi.py         (伺服器佈署設定檔)
     |             |____ asgi.py         (伺服器佈署設定檔, 非同步)
     |____ templates                   (專案的模板資料夾)
     |____ myapp1                      (應用程式目錄)
                   |____ migrations     (資料庫同步)
                   |____ __init__.py    (形成套件)
                   |____ admin.py       (註冊資料模型)
                   |____ apps.py         (App 設定檔)
                   |____ models.py     (資料模型定義)
                   |____ tests.py          (測試用程式)
                   |____ views.py        (App 視圖模組, 自動建立)
                   |____ urls.py           (App 路由模組, 手工添加)
                   |____ templates      (App 的模板資料夾)


注意, 專案的網頁模板資料夾 templates 是放在上層專案目錄下, 與下層專案目錄, App 等位於同意層; 而 App 的 templates 則放在 App 目錄下. 


一. App 目錄下的模板與控制器設定 : 

此處使用模板網頁來改寫前一篇 App=myapp1 的兩個請求 (根目錄 myapp1/ 與打招呼 myapp1/helloworld), 如前所述, 為了能動態地傳遞變數, 將打招呼的請求 URL 改為 myapp1/hello. 

首先在 myapp1/templates 下建立兩個網頁模板 myapp1_home.htm 與 hello.htm 來改寫上一篇測試中 myapp1/ 與 myapp1/hello 的請求處理. myapp1_home.htm 是 myapp1 的首頁, 內容如下 :

<!-- myapp1_home.htm -->
<!DOCTYPE html>
<html>
<head>
  <title>Hello</title>
  <meta charset="utf-8">
</head>
<body>
  <h2>這是 App1 首頁!</h2>
</body>
</html>

hello.htm 是可傳入變數, 顯示動態資訊的網頁模板, 內容如下 :

<!-- hello.htm -->
<!DOCTYPE html>
<html>
<head>
  <title>Hello</title>
  <meta charset="utf-8">
</head>
<body>
  <h2>Hello, {{name}}, 現在的時間是 {{now}}</h2>
</body>
</html>

此模板網頁中以雙重大括號括起來的地方是模板引擎讓視圖模組 views.py 傳出的變數嵌入之處, 模板網頁中可以嵌入變數, 標籤, 以及註解 :


 模板語法元素 說明
 {{ 變數 & 過濾器 }} 來自 views.py 的變數, 用來直接替換該位置內容或過濾
 {% 標籤 %} 標籤用來控制資料顯示邏輯 (迴圈與判斷)
 {# 註解 #} 註解會被模板引擎忽略


但其實真正有作用的模板語法只有 {{}} 與 {%%} 這兩個而已, 其中  {%%} 最重要, 可使用 Python 的 if elif else 語法進行分支判斷, 也可以使用與 Python for 迴圈幾乎一樣的語法來進行迭代. 

分支判斷的模板語法如下 : 
 

 單一分支 雙重分支 多重分支
 {% if 條件式 %}
 輸出網頁
 (% endif %}
 {% if 條件式 %}
 輸出網頁 1
 {% else %}
 輸出網頁 2
 (% endif %}
 {% if 條件式 %}
 輸出網頁 1
 {% elif %}
 輸出網頁 2
 {% elif %}
 輸出網頁 3
 {% else %}
 輸出網頁 4
 (% endif %}


for 迴圈的模板語法如下 :


 模板 for 迴圈 說明
 {% for 變數 in 可迭代變數 %}
 內含 {{變數}} 的網頁
 {% endfor %}
 用 for in 迭代內含 {{變數}} 的網頁
 {% for 變數 in 可迭代變數 reversed%}
 內含 {{變數}} 的網頁
  {% endfor %}
 
 以倒序方式迭代內含 {{變數}} 的網頁
 {% for 變數 in 可迭代變數%}
 內含 {{變數}} 的網頁 1
 {% empty %}
 迴圈為空時輸出的網頁 2 
 {% endfor %}
 
 可迭代變數不為空時輸出網頁 1, 為空時輸出網頁 2
 注意, empty 必須放在 endfor 前面. 


除此之外, 模板語法還提供迴圈變數 (物件) forloop, 其屬性可用在 for 迴圈中讓 if elif else 分支判斷語法決定是否要輸出特定網頁內容 : 
 

 迴圈變數 forloop 的屬性 說明
 forloop.counter 迴圈計數器 (1 起始)
 forloop.counter0 迴圈計數器 (0 起始)
 forloop.revcounter 倒數之迴圈計數器 (由總圈數遞減至 1)
 forloop.revcounter0 倒數之迴圈計數器 (由總圈數遞減至 0)
 forloop.first 是否為第一個迴圈 (True/False)
 forloop.last 是否為最後一個迴圈 (True/False)
 forloop.parentloop 上一層迴圈的 forloop 變數


詳細用法參考 :


其次編輯 myapp1 的視圖模組 views.py, 注意, 與前一篇測試不同之處是, 因為要透過在 URL 中傳遞變數來動態顯示打招呼的人名, 故 URL 函式名稱由 helloworld 改為 hello :

# views.py of App=myapp1
from django.shortcuts import render   
from datetime import datetime

def hello(request, name='匿名'):   # URL='myapp1/hello/' 的處理函式
    now=datetime.now()
    return render(request, 'hello.htm', {'name': name, 'now': now})

def myapp1_home(request):     # App 根目錄 URL= 'myapp1/' 的處理函式
    return render(request, 'myapp1_home.htm')   

此處須匯入 django.shortcuts.render() 來輸出回應, 此函式可透過模板引擎對模板網頁傳遞變數, 其語法如下 : 

render(request, '模板網頁檔名', 變數字典)    

其中第一個參數為 http.HttpRequest 物件實例, 代表一個 URL 請求, 第二個參數為模板網頁檔名, 例如上面建立的 'myapp1_home.htm' 或 'hello.htm', 第三個參數是要傳給模板網頁的變數字典, 如果要一一列舉太麻煩, 也可呼叫 Python 內建函式 locals(), 此函式會將 views.py 裡面用到的變數組成字典傳回, 所以上面的 views.py 也可以改寫為如下較簡短的程式碼 :

# views.py of App=myapp1
from django.shortcuts import render   
from datetime import datetime

def hello(request, name='匿名'):   # URL='myapp1/hello/<name>' 的處理函式 (name 有預設值)
    now=datetime.now()       # 傳回現在日期時間
    return render(request, 'hello.htm', locals())

def myapp1_home(request):     # App 根目錄 URL= 'myapp1/' 的處理函式
    return render(request, 'myapp1_home.htm')   

注意 這裡 hello() 需列舉 URL 會攜帶的所有參數, 目前只有 1 個 : name, 因為客戶端也可能不會傳參數, 故 name 一個預設值 '匿名', 它會跟 now 變數一起被 locals() 做成變數字典傳給 render(). 

接著改寫 myapp1 的路由模組 urls.py (App 的 uls.py 需自建, 可從下層專案模組目錄 mysite 底下複製專案的 urls.py 來改) :

# urls.py of App=myapp1
from django.urls import path
from . import views      # 匯入 App 的視圖模組

urlpatterns = [
    path('hello/<str:name>/', views.hello),  # 有參數
    path('hello/', views.hello),  # 無參數 
    path('', views.myapp1_home),  # myapp1 的根目錄請求
]

此處主要是從 App 目錄下匯入 myapp1 自己的視圖模組 views.py, 然後分別將 'myapp1/hello' 與 'myapp1/' 的路由請求分別委派給 views.py 裡的 hello() 與 myapp1_home() 函式處理. 注意, 這裡 hellp 有兩個 path, 一個是有帶參數 name 的, 一個則無. 

注意, App 路由模組中的 URL 都是相對於 'myapp1/', 故 'hello/' 在客戶端要用 'myapp1/hello/' 請求; 而 App 跟目錄 '' (空字串) 在客戶端要用 'myapp1/' 請求. 

以上就完成 App 的控制器設定了. 


二. 專案目錄下的模板與控制器設定 : 

專案目錄 (注意是上層) templates 下的網頁模板主要是用來輸出網站首頁 (避免與 App 綁在一起以提升 App 可移植性), 與前一篇只簡單地顯示 ''歡迎來到我的首頁!' 不同的是, 此處要跟上面 myapp1 的 hello 一樣傳遞變數 (此處為傳遞目前的時間資訊) 給模板網頁, 首先在上層專案目錄的 templates 資料夾下建立一個網頁模板 home.htm :

<!-- home.htm -->
<!DOCTYPE html>
<html>
<head>
  <title>首頁</title>
  <meta charset="utf-8">
</head>
<body>
  <h2>歡迎來到我的首頁! 現在的時間是 {{now}}</h2>
</body>
</html>

此首頁除了顯示打招呼用語外, 還會顯示從專案的視圖模組 views.py 傳遞過來的 now 變數. 

接著編輯專案的視圖模組 views.py, 由於建立專案時不會在下層專案目錄中自動建立 views.py, 須自建或從 App 目錄下複製 myapp1 的 views.py 來修改 : 

# views.py of project=mysite
from django.shortcuts import render
from datetime import datetime

def home(request):  
    now=datetime.now()       # 傳回現在日期時間
    return render(request, 'home.htm', locals())     

此處僅定義一個 home() 函式來處理網站首頁 (根目錄) 的請求, 將變數傳給模板網頁回應給客戶端. locals() 會傳回此函式內的區域變數為一個字典 (即 {'now': now}) 傳遞給模板網頁 home.htm 顯示給客戶端. 

接著編輯專案路由模組 urls.py (這模組會在建立專案時自動產生, 不須自建), 內容與前一篇的 urls.py 一樣不用修改 : 

# urls.py of project=mysite
from django.contrib import admin
from django.urls import path, include  # include() 用來含括各 App 的 urls.py
from . import views   # 專案的視圖模組

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home),   # 將網站首頁請求委派給專案的視圖模組
    # 將 URL='myapp1/' 委派給 myapp1 的 urls.py
    path('myapp1/', include('myapp1.urls')),    
]

這樣就完成了專案控制器的設定. 


三. 專案模板資料夾位置設定 : 

最後必須開啟專案設定檔 settings.py, 設定專案模板的位置, 這樣才能正確取得專案模板網頁路徑並順利輸出網站首頁. 

開啟 settings.py 後搜尋 TEMPLATES, 裡面 DIR 鍵的值預設為空串列 : 

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

此 DIR 就是用來設定專案模板位置的, 請在空串列中填入 BASE_DIR/'templates' :

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR/'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

其中 BASE_DIR 就是上層專案目錄路徑, 在 settings.py 中搜尋 BASE_DIR 即可找到其值如下 :

BASE_DIR = Path(__file__).resolve().parent.parent

__file__ 表示 settings.py 本身, 經呼叫 resolve() 解析其絕對位置後, 因 settings.py 位於下層專案目錄 mysite 下, 故 .parent 為下層專案目錄之路徑, 再一次 .parnt 即取得上層專案目錄路徑, 而我們的專案模板資料夾 templates 即位於上層專案目錄 mysite 底下, 串接後即得專案模板資料夾 templates 路徑. 注意, 這裡必須使用字串 'templates' 而非變數 template. 

至於 App 的模板位置不需特別設定, 因為 'DIR' 鍵底下的 'APP_DIR' 預設為 True, 即 Django 會自動到各 App 資料夾下找尋該 App 的 templates 資料夾. 

至此完成所有網站與 App 設定, 用下列指令啟動開發伺服器 : 

python manage.py runserver   

然後用瀏覽器拜訪上面專案與 App 的 urls.py 所定義之 URL : 

http://127.0.0.1:8000      (網站根目錄: 首頁)
http://127.0.0.1:8000/myapp1       (App 根目錄)
http://127.0.0.1:8000/myapp1/hello       (App 的 hello: 無參數)
http://127.0.0.1:8000/myapp1/hello/Tony       (App 的 hello: 有參數)

結果如下 : 




範例檔可從 GitHub 下載 :


以上的測試總結如下 :
  • 專案與 App 都可以有各自的 templates 網頁模板資料夾, 讓 App 與專案的耦合度降低以提升App 的可移植性, 在開發大型專案時能達成較高的元件可重用性. 
  • 專案的 templates 模板資料夾建立後必須開啟專案設定檔 settings.py, 將 TEMPLATES 變數的 DIR 串列設為 [BASE_DIR/'templates'], 這樣 Django 的模板引擎才找得到專案的模板資料夾路徑. App 的模板資料夾位置不需特別設定, 因為 TEMPLATES 變數的 APP_DIR 屬性預設為 True, 模板引擎會自動到各 App 目錄下尋找 templates 目錄. 
  • 若不考慮 App 的移植性, 也可以只用專案的網頁模板資料夾, 不設 App 的 templates, 亦即全部 App 都使用專案模板資料夾. 


四. 模板網頁的匯入與繼承 : 

為了模板網頁能達成模組化與結構化要求, 模板引擎還提供了模板網頁匯入 (include 指令) 與繼承 (extend 指令) 的語法將其他的模板網頁或片段引入到目前的模板網頁中, 兩者差別如下 :
  • 被 include 的模板網頁通常是被切割後的網頁片段而非完整的網頁, 但被繼承的模板網頁一定是完整的網頁. 
  • 繼承指令 {% extend %} 必須放在模板網頁最前面; 而匯入指令 {% include %} 則可以在模板網頁的任何地方. 
使用模板引擎的 {% include %} 指令可以在模板網頁中匯入其他網頁檔案, 語法如下 : 

{% include "其他模板網頁.htm" %} 

注意, 這裡 "其他模板網頁.htm" 檔名必須用引號括起來 (單雙均可), 例如下列模板網頁 :

<!--index.htm-->
<!DOCTYPE html>
<html>
<head>
  <title>Hello</title>
  <meta charset="utf-8">
</head>
<body>
  <h1>Hello World</h1>   
</body>
</html>

可將其中 body 的內容單獨寫成一個 helloworld.htm 檔 : 

<!--helloworld.htm-->
<h1>Hello World</h1>

然後修改模板 index.htm, 在其中用 include 匯入 helloworld.htm :

<!--index.htm-->
<!DOCTYPE html>
<html>
<head>
  <title>Hello</title>
  <meta charset="utf-8">
</head>
<body>
  {% include "helloworld.htm" %}    
</body>
</html>

注意, 被匯入的 hellowrld.htm 不是完整的 html 格式, 而只是一個網頁片段. 但這種將網頁片段作為一個 htm 檔的作法有點怪, 模板網頁模組化主要還是靠繼承 (extend). 

模板網頁的繼承與類別的繼承概念類似, 被繼承的模板稱為父模板, 繼承者稱為子模板. 在父模板中會用下列語法定義若干讓子模板嵌入網頁片段的區塊, 並為這些區塊命名 :

{% block 區塊名稱 %}{% endblock %}    

子模板則必須在檔案一開頭便使用下列語法繼承父模板 :

{% extends '父模板.htm' %}    

然後用下列語法在所繼承之父模板中的指定區塊嵌入網頁片段 : 

{% block 區塊名稱 %}
要嵌入之網頁片段
{% endblock %}    

例如我們可以先建立一個基礎網頁模板 base.htm 當作所有網頁模板的父模板 :

<!--base.htm-->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{% block title %}{% endblock %}</title>
  {% block link %}{% endblock %}
  {% block script %}{% endblock %}
  <style>
  {% block style %}{% endblock %}
  </style>
</head>
<body>
 {% block body %}{% endblock %}
</body>
</html>

上面的黃色部分就是讓繼承 base.htm 的子模板網頁用 block 指令於該處嵌入樣式, 程式檔, 或網頁片段的地方, 這樣子模板網頁就不需要重複寫 HTML 相同的部分, 結構就變得很簡單 :

{% extends "base.htm" %}
{% block title %}網頁標題{% endblock %}
{% block link %}
   link 樣式檔
{% endblock %}
{% block script %}
   script 程式檔
{% endblock %}
{% block body %}
   網頁內容 + script 程式檔
{% endblock %}

注意, extends 指令一定要放在模板網頁最前面 (忽略註解). 其次, Javascript 程式檔在網頁中的位置可以在 head 內, 也可以在 body 內, 在 body 中匯入時不另外做一個嵌入區塊, 而是與網頁內容合在一起嵌入. 

例如上面的 index.htm  就可以寫成 : 

<!--index.htm-->
{% extends "base.htm" %}
{% block title %}Hello{% endblock %}
{% block body %}
   <h1>Hello World</h1>   
{% endblock %}
  
注意, base.htm 裡面的 link 與 script 區塊若沒用到就不用嵌入任何資料. 

這樣就可以用模板繼承方式來改寫上面的 mysite 網站了, 首先在上層專案目錄的模板資料夾 templates 底下新增基底網頁模板 base.htm, 然後修改網站的首頁檔 home.htm 為如下 :

<!-- home.htm -->
{% extends "base.htm" %}
{% block title %}首頁{% endblock %}
{% block body %}
  <h4>歡迎來到我的首頁! 現在的時間是 {{now}}</h4>
{% endblock %}

然後切換到應用程式 myapp1 下的 templates 資料夾下, 將原本的兩個模板網頁改成繼承自專案基底模板 base.htm, 下面是修改後的 hello.htm 模板 : 

<!-- hello.htm -->
{% extends "base.htm" %}
{% block title %}Hello{% endblock %}
{% block body %}
  <h4>Hello, {{name}}, 現在的時間是 {{now}}</h4>
{% endblock %}

下面是修改後的 myapp1_home.htm 模板 : 

<!-- myapp1_home.htm -->
{% extends "base.htm" %}
{% block title %}myapp1 首頁{% endblock %}
{% block body %}
  <h4>這是 App1 首頁!</h4>
{% endblock %}

結果與上面未使用模板繼承的網站是一樣的. 以上修改後的範例下載網址 :



五. 利用模板繼承使用網頁框架 : 

利用上面的模板繼承功能可以讓網站在使用框架時更方便, 此處以 Bootstrap 4 為例, 使用此框架必須匯入 Bootstrap 的程式與 CSS 樣式表, 以及 jQuery 框架, 因為 Bootstrap 的互動功能是依賴 jQuery 達成的 : 

  <link rel="stylesheet" href="https://unpkg.com/bootstrap@4.0.0/dist/css/bootstrap.min.css">
  <script src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script>
  <script src="https://unpkg.com/bootstrap@4.0.0/dist/js/bootstrap.min.js"></script>

關於 Bootstrap 用法參考 :


同樣地, 模板網頁可以繼承專案 templates 的父模板, 也可以繼承應用程式本身 templates 下的父模板, 首先在專案 templates 下建立一個繼承 base.htm 的 bootstrap.htm 模板 :

<!--bootstrap.htm-->
{% extends "base.htm" %}
{% block link %}
  <link rel="stylesheet" href="https://unpkg.com/bootstrap@4.0.0/dist/css/bootstrap.min.css">
{% endblock %}
{% block script %}
  <script src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script>
  <script src="https://unpkg.com/bootstrap@4.0.0/dist/js/bootstrap.min.js"></script>
{% endblock %}
 
此 Bootstrap 的框架模板在繼承 base.htm 時僅在 head 中嵌入 link 與 script 這兩個區塊, 而 title 與 body 區塊則未嵌入, 而是讓繼承 bootstrap.htm 的子模板來嵌入. 然後修改網站首頁 home.htm, 讓它從直接繼承 base.htm 改為繼承 bootstrap.htm, 並且使用 Bootstrap 的 alert-info 樣式來顯示首頁內容 :

<!-- home.htm -->
{% extends "bootstrap.htm" %}
{% block title %}首頁{% endblock %}
{% block body %}
<div class="container">
  <div class="alert alert-info" role="alert">
歡迎來到我的首頁! 現在的時間是 {{now}}
  </div>
</div>
{% endblock %}

然後修改應用程式 templates 下的 hello.htm 與 myapp1_home.htm, 同樣也是繼承專案 templates 下的 bootstrap.htm : 

<!-- hello.htm -->
{% extends "bootstrap.htm" %}
{% block title %}Hello{% endblock %}
{% block body %}
<div class="container">
  <div class="alert alert-warning" role="alert">
  Hello, {{name}}, 現在的時間是 {{now}}
  </div>
</div>
{% endblock %}

<!-- myapp1_home.htm -->
{% extends "bootstrap.htm" %}
{% block title %}myapp1 首頁{% endblock %}
{% block body %}
<div class="container">
  <div class="alert alert-danger" role="alert">
  這是 App1 首頁!
  </div>
</div>
{% endblock %}

結果如下 :




可見 Bootsrap 的效果樣式效果有出來了. 此範例下載網址 :


Bootstrap 框架的模板不一定要放在專案的 templates 下, 也可以放在應用程式的 templates 資料夾下, 專門給該 App 的其他子模板網頁繼承之用, 這樣可以提高 App 的可移植性, 首先在 myapp1/templates 下建立應用程式的 Bootstrap 模板 : 

<!--myapp1_bootstrap.htm-->
{% extends "base.htm" %}
{% block link %}
  <link rel="stylesheet" href="https://unpkg.com/bootstrap@4.0.0/dist/css/bootstrap.min.css">
{% endblock %}
{% block script %}
  <script src="https://unpkg.com/jquery@3.4.1/dist/jquery.min.js"></script>
  <script src="https://unpkg.com/bootstrap@4.0.0/dist/js/bootstrap.min.js"></script>
{% endblock %}

然後修改 myapp1 的兩個子模板, 將其父模板從專案的 bootstrap.htm 改成應用程式自己的 Bootstrap 模板 myapp1_bootstrap.htm : 

<!-- myapp1_home.htm -->
{% extends "myapp1_bootstrap.htm" %}
{% block title %}myapp1 首頁{% endblock %}
{% block body %}
<div class="container">
  <div class="alert alert-success" role="alert">
  這是 App1 首頁!
  </div>
</div>
{% endblock %}

此處除了父模板改成 myapp1_bootstrap.htm 外, 還將 alert 樣式改成 alert-success, 以與前一個範例作區別. 結果如下 : 




此例只動了 myapp1 的模板網頁, 網站首頁沒變, 仍然是 alert-info, 但 myapp1 的模板網頁都改成 alert-success, 可見這兩個網頁確實是繼承 myapp1_bootstrap.htm, 此範例下載網址 : 


上面範例雖然演示了 Bootstrap 框架父模板網頁既可以放在專案的 templates 目錄中供專案本身與所有 App 繼承使用; 也可以放在各 App 的 templates 目錄下供自己使用, 但一般來說網站會有統一的風格樣式, 故實務上會放在專案 templates 底下. 

沒有留言 :