步驟 3:提供靜態檔案、新增頁面,然後搭配 Flask 應用程式使用範本繼承

上一個步驟:使用檢視與頁面範本來建立 Flask 應用程式

在本教學課程的先前步驟中,您已學會如何建立具有單一獨立式 HTML 頁面的精簡 Flask 應用程式。 現代化 Web 應用程式通常是由許多網頁所組成,並使用 CSS 和 JavaScript 檔案等共用資源來提供一致的樣式和行為。

在這個步驟中,您將了解如何:

  • 使用 Visual Studio 項目範本,為可重複使用的程式碼 (步驟 3-1) 新增不同類型的檔案
  • 從程式碼提供靜態檔案 (步驟 3-2,選擇性)
  • 將額外頁面加入應用程式 (步驟 3-3)
  • 使用範本繼承,以建立跨頁面使用的標題和瀏覽列 (步驟 3-4)

步驟 3-1:熟悉項目範本

當您開發 Flask 應用程式時,通常會新增許多 Python、HTML、CSS 和 JavaScript 檔案。 針對每一種檔案類型 (以及開發可能需要的其他檔案,例如 web.config),Visual Studio 都提供方便您著手的項目範本

若要查看可用的範本,請移至 [方案總管],以滑鼠右鍵按一下您要建立項目的資料夾,選取 [加入]>[新項目]

Add new item dialog in Visual Studio

若要使用範本,請選取所需的範本、指定檔案名稱,然後選取 [確定]。 以這種方式加入項目,會將檔案自動加入您的 Visual Studio 專案,並為原始檔控制標記變更。

問題:Visual Studio 如何知道應提供哪些項目範本?

回答:Visual Studio 專案檔 (.pyproj) 包含將它標示為 Python 專案的專案類型識別碼。 Visual Studio 會使用此類型的識別碼,以便只顯示適用於專案類型的項目範本。 如此一來,Visual Studio 便可為許多專案類型提供豐富的項目範本,而無須要求您每次都要查看整理範本。

步驟 3-2:從您的應用程式提供靜態檔案

在使用 Python (使用任何架構) 建置的 Web 應用程式中,您的 Python 檔案一律在 Web 主機伺服器上執行,絕對不會傳輸到使用者的電腦。 其他檔案 (例如 CSS 和 JavaScript) 只由瀏覽器使用,因此主機伺服器只會以原樣來傳遞這些檔案。 這類檔案稱為「靜態」檔案,且 Flask 可以自動傳遞這些檔案,而無需您撰寫任何程式碼。 例如,在 HTML 檔案內,您可以使用專案中的相對路徑來參考靜態檔案。 此步驟的第一個部分會將 CSS 檔案新增到您現有的頁面範本。

需要從程式碼傳遞靜態檔案 (例如透過 API 端點實作) 時,Flask 會提供一個便利的方法,讓您使用名為 static 的資料夾 (在專案根目錄中) 中相對路徑來參考檔案。 此步驟的第二個部分會使用一個簡單的靜態資料檔來示範該方法。

在上述任一情況下,只要您喜歡,都可以將檔案組織在 static 下。

在範本中使用靜態檔案

  1. 在 [方案總管] 中,於 Visual Studio 專案的 [HelloFlask] 資料夾上按一下滑鼠右鍵,選取 [新增]>[新增資料夾],然後將資料夾命名為 static

  2. 以滑鼠右鍵按一下 [static] 資料夾,並選取 [新增]>[新增項目]。 在出現的對話方塊中,選取 [Stylesheet] (樣式表) 範本,將檔案命名為 site.css,然後選取 [確定]site.css 檔案會出現在專案中,並在編輯器中開啟。 您的資料夾結構應該與下列影像類似:

    Static file structure as shown in Solution Explorer

  3. site.css 的內容取代為下列程式碼並儲存檔案:

    .message {
        font-weight: 600;
        color: blue;
    }
    
  4. 將應用程式 templates/index.html 檔案的內容取代為下列程式碼,其中會將步驟 2 中所使用的 <strong> 項目取代為參考 message 樣式類別的 <span>。 以這種方式使用樣式類別,可讓您在設定元素的樣式時獲得更大彈性。

    <html>
        <head>
            <title>{{ title }}</title>
            <link rel="stylesheet" type="text/css" href="/static/site.css" />
        </head>
        <body>
            <span class="message">{{ message }}</span>{{ content }}
        </body>
    </html>
    
  5. 執行專案,以觀察其結果。 完成時,請停止應用程式,然後可將變更認可至原始檔控制 (如步驟 2所述)。

從程式碼提供靜態檔案

Flask 提供一個稱為 serve_static_file 的函式,您可以從程式碼呼叫此函式來參考專案 static 資料夾內的任何檔案。 下列程序會建立一個簡單的 API 端點,傳回靜態資料檔案。

  1. 如果您尚未建立 static 資料夾,請建立此資料夾:在 [方案總管] 中,於 Visual Studio 專案的 [HelloFlask] 資料夾上按一下滑鼠右鍵,選取 [新增]>[新增資料夾],然後將資料夾命名為 static

  2. static 資料夾中,使用下列內容 (無意義的範例資料) 來建立一個名為 data.json 的 JSON 資料檔:

    {
        "01": {
            "note" : "Data is very simple because we're demonstrating only the mechanism."
        }
    }
    
  3. views.py 中,新增一個具有 /api/data 路由且使用 send_static_file 方法來傳回靜態資料檔的函式:

    @app.route('/api/data')
    def get_data():
      return app.send_static_file('data.json')
    
  4. 執行應用程式,然後瀏覽至 /api/data 端點以查看是否已傳回靜態檔案。 完成時,請停止應用程式。

問題:組織靜態檔案有任何慣例嗎?

回答:您可以依偏好將其他的 CSS、JavaScript 和 HTML 檔案新增至您的 static 資料夾。 組織靜態檔案的一般方式是建立名為 fontsscriptscontent 的子資料夾 (針對樣式表和任何其他檔案)。

問題:如何處理 API 中的 URL 變數和查詢參數?

回答:請參閱步驟 1-4 中問題:Flask 如何與變數 URL 路由和查詢參數搭配運作?的回答

步驟 3-3:將頁面加入應用程式

將其他頁面加入應用程式意義如下:

  • 加入定義檢視的 Python 函式。
  • 加入網頁標記的範本。
  • 將必要的路由新增至 Flask 專案的 urls.py 檔案。

下列步驟會將 [About] (關於) 頁面新增至 "HelloFlask" 專案,並從首頁連結至該頁面:

  1. 在 [方案總管] 中,以滑鼠右鍵按一下 [templates] 資料夾,選取 [新增]>[新增項目]選取 [HTML Page] (HTML 頁面) 項目範本、將檔案命名為 about.html,然後選取 [確定]

    提示

    如果 [新增項目] 命令未出現在 [新增] 功能表上,請確定您已停止應用程式,如此 Visual Studio 才會結束偵錯模式。

  2. about.html 的內容取代為下列標記 (您會在步驟 3-4 中將首頁的明確連結取代為簡單的導覽列):

    <html>
        <head>
            <title>{{ title }}</title>
            <link rel="stylesheet" type="text/css" href="/static/site.css" />
        </head>
        <body>
            <div><a href="home">Home</a></div>
            {{ content }}
        </body>
    </html>
    
  3. 開啟應用程式的 views.py 檔案,然後新增使用範本且名為 about 的函式:

    @app.route('/about')
    def about():
        return render_template(
            "about.html",
            title = "About HelloFlask",
            content = "Example app page for Flask.")
    
  4. 開啟 templates/index.html 檔案,然後將下列這一行新增到 <body> 項目內,以連結到 [About] \(關於\) 頁面 (同樣地,您會在步驟 3-4 中以導覽列取代此連結):

    <div><a href="about">About</a></div>
    
  5. 使用 [檔案]>[全部儲存] 功能表命令來儲存所有檔案,或者按下 Ctrl+Shift+S。 (不需要此步驟,因為在 Visual Studio 中執行專案會自動儲存檔案。不過,知道有這個命令也很好!)

  6. 執行專案以觀察結果並檢查頁面之間的瀏覽。 完成時,請停止應用程式。

問題:頁面函式的名稱對 Flask 來說是否重要?

回答:否,因為它是 @app.route 裝飾項目,用來判斷 Flask 為其呼叫函式以產生回應的 URL。 開發人員通常會讓函式名稱與路由相符,但這類相符並非必要。

步驟 3-4:使用範本繼承來建立標題和瀏覽列

現代化 Web 應用程式通常使用品牌標題和導覽列來提供最重要的頁面連結、快顯功能表等等,而不會在每個頁面上提供明確的導覽連結。 為了確保應用程式一致,標頭和瀏覽列在所有頁面上都應該相同,但不會在每個頁面範本中重複相同的程式碼。 反之,您應該是在同一個地方定義所有頁面的共同部分。

Flask 的範本化系統 (預設為 Jinja) 提供兩種方法,可跨多個範本重複使用特定元素:包含和繼承。

  • 「包含」使用 {% include <template_path> %} 語法,在參考範本中的特定位置插入其他頁面範本。 如果您想要以動態方式變更程式碼中的路徑,也可以使用變數。 「包含」通常用在頁面主體,用來在頁面特定位置引進共用的範本。

  • 「繼承」在頁面範本開頭使用{% extends <template_path> %},以指定共用基底範本,以便參考範本並接著據以建置範本。 繼承通常用來定義共用版面配置、瀏覽列和應用程式頁面的其他結構,因此參考範本只需要加入或修改名為 blocks (區塊) 的基底範本特定區域。

在這兩種情況下,<template_path> 是相對於應用程式的 templates 資料夾 (也允許 .././)。

基底範本使用 {% block <block_name> %}{% endblock %} 來描述「區塊」。 若之後參考範本再對同一個區塊名稱使用標籤,其區塊內容將會覆寫基底範本的區塊內容。

下列步驟將示範繼承:

  1. 在應用程式的 templates 資料夾中,建立稱為 layout.html 的新 HTML 檔案 (使用 [新增]>[新增項目] 操作功能表,或 [新增]>[HTML 網頁]),然後以下列標記取代其內容。 您可以看到此範本包含一個名為 "content" (內容) 的區塊,這是參考頁面必須全部取代的部分:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>{{ title }}</title>
        <link rel="stylesheet" type="text/css" href="/static/site.css" />
    </head>
    
    <body>
        <div class="navbar">
            <a href="/" class="navbar-brand">Hello Flask</a>
            <a href="{{ url_for('home') }}" class="navbar-item">Home</a>
            <a href="{{ url_for('about') }}" class="navbar-item">About</a>
        </div>
    
        <div class="body-content">
            {% block content %}
            {% endblock %}
            <hr/>
            <footer>
                <p>&copy; 2018</p>
            </footer>
        </div>
    </body>
    </html>
    
  2. 將下列樣式新增至應用程式的 static/site.css 檔案(本逐步解說不會示範回應式設計。不過,這些樣式只會產生有趣的結果):

    .navbar {
        background-color: lightslategray;
        font-size: 1em;
        font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
        color: white;
        padding: 8px 5px 8px 5px;
    }
    
    .navbar a {
        text-decoration: none;
        color: inherit;
    }
    
    .navbar-brand {
        font-size: 1.2em;
        font-weight: 600;
    }
    
    .navbar-item {
        font-variant: small-caps;
        margin-left: 30px;
    }
    
    .body-content {
        padding: 5px;
        font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
    
  3. templates/index.html 修改成參考基底範本,並覆寫內容區塊。 如您所見,使用繼承會讓此範本變得更簡單:

    {% extends "layout.html" %}
    {% block content %}
    <span class="message">{{ message }}</span>{{ content }}
    {% endblock %}
    
  4. templates/about.html 修改成亦參考基底範本,並覆寫內容區塊:

    {% extends "layout.html" %}
    {% block content %}
    {{ content }}
    {% endblock %}
    
  5. 執行伺服器,以觀察其結果。 當您完成時,請關閉伺服器。

    Running app showing the nav bar

  6. 由於您對應用程式做了大幅變更,因此現在也是將變更認可至原始檔控制的好時機。

下一步

您可以更深入地利用這些資源: