Django 亮點:模板化保存行(第 2 部分)
已發表: 2022-03-10一些構建網站的簡單方法需要開發人員手動編寫 HTML 的每一行。 在另一個極端,商業無代碼站點構建器會自動為用戶創建所有 HTML,通常以犧牲生成代碼的可讀性為代價。 模板是該範圍的中間部分,但更接近於手寫 HTML,而不是使用 React 或類似庫在單頁應用程序中生成頁面結構。 連續統一體上的這個最佳點提供了從頭開始手動 HTML 的許多好處(語義/可讀代碼、對頁面結構的完全控制、快速頁面加載)並增加了關注點和簡潔性的分離,所有這些都以花費一些時間編寫為代價手動修改 HTML。 本文演示了使用 Django 模板編寫複雜頁面。

今天的主題適用於 Django 框架之外。 Flask(另一個 Web 框架)和 Pelican(一個靜態站點生成器)只是使用相同模板方法的許多其他 Python 項目中的兩個。 Jinja2 是所有三個框架都使用的模板引擎,儘管您可以通過更改項目設置來使用不同的引擎(嚴格來說,Jinja2 是 Django 模板的超集)。 它是一個獨立的庫,即使沒有框架,您也可以將其合併到您自己的項目中,因此本文中的技術非常有用。
該系列的先前部分:
- Django 亮點:用戶模型和身份驗證(第 1 部分)
- Django 亮點:模型、管理和利用關係數據庫(第 3 部分)
- Django 亮點:處理靜態資產和媒體文件(第 4 部分)
服務器端渲染
模板只是一個 HTML 文件,其中 HTML 已使用附加符號進行了擴展。 記住HTML代表什麼:超文本標記語言。 Jinja2,我們的模板語言,只是簡單地在語言中添加了額外的有意義的標記符號。 當服務器呈現模板以向用戶提供純 HTML 頁面時,會解釋這些附加結構(也就是說,來自模板語言的附加符號不會進入最終輸出)。
服務器端渲染是響應請求而構建網頁的過程。 Django 使用服務器端渲染向客戶端提供 HTML 頁面。 在其執行結束時,視圖函數將 HTTP 請求、一個或多個模板以及在函數執行期間訪問的可選數據組合起來,以構建一個 HTML 頁面,並將其作為響應發送給客戶端。 數據從數據庫通過視圖進入模板以到達用戶。 如果這個抽象的解釋不完全有意義,請不要擔心,我們將在本文的其餘部分轉向一個具體的例子。
配置
對於我們的示例,我們將使用一個相當複雜的網頁,啟動 Bootstrap 的管理模板,並將 HTML 重寫為 Jinja2 模板。 請注意,MIT 許可的庫使用不同的模板系統(基於 JavaScript 和 Pug)來生成您看到的頁面,但它們的方法與 Jinja2 樣式的模板有很大不同,所以這個例子更像是逆向工程而不是翻譯他們優秀的開源項目。 要查看我們將要構建的網頁,您可以查看 Start Bootstrap 的實時預覽。
我為本文準備了一個示例應用程序。 要讓 Django 項目在您自己的計算機上運行,請啟動您的 Python 3 虛擬環境,然後運行以下命令:
pip install django git clone https://github.com/philipkiely/sm_dh_2_dashboard.git cd sm_dh_2_dashboard python manage.py migrate python manage.py createsuperuser python manage.py loaddata employee_fixture.json python manage.py runserver
然後,打開您的網絡瀏覽器並導航到https://127.0.0.1:8000
。 您應該看到與預覽相同的頁面,與下圖匹配。

因為本教程專注於前端,所以底層的 Django 應用程序非常簡單。 這似乎是用於呈現單個網頁的大量配置,而且公平地說。 然而,如此多的設置也可以支持更強大的應用程序。
現在,我們已準備好完成將這個 668 行 HTML 文件轉換為適當架構的 Django 站點的過程。
模板和繼承
將數百行 HTML 重構為乾淨代碼的第一步是將元素拆分為自己的模板,Django 將在渲染步驟中將這些模板組合成一個網頁。
看看pages/templates 。 您應該看到五個文件:
- base.html ,每個網頁都將擴展的基本模板。 它包含帶有標題、CSS 導入等的
<head>
。 - navbar.html ,頂部導航欄的 HTML,必要時包含的組件。
- footer.html ,頁面頁腳的代碼,需要時包含的另一個組件。
- sidebar.html ,側邊欄的 HTMl,需要時包含的第三個組件。
- index.html ,主頁面獨有的代碼。 此模板擴展了基本模板並包括三個組件。
Django 像 Voltron 一樣組裝這五個文件來呈現索引頁面。 允許這樣做的關鍵字是{% block %}
、 {% include %}
和{% extend %}
。 在base.html中:
{% block content %} {% endblock %}
這兩行為擴展base.html以插入自己的 HTML 的其他模板留出了空間。 請注意, content
是一個變量名,您可以在一個模板中擁有多個不同名稱的塊,從而為子模板提供靈活性。 我們看到如何在index.html中擴展它:
{% extends "base.html" %} {% block content %} <!-- HTML Goes Here --> {% endblock %}
使用帶基本模板名稱的extends
關鍵字為索引頁面提供了結構,使我們免於在標題中復制(請注意,文件名是雙引號字符串形式的相對路徑)。 索引頁麵包括網站上大多數頁面共有的所有三個組件。 我們引入了帶有include
標籤的組件,如下所示:
{% extends "base.html" %} {% block content %} {% include "navbar.html" %} {% include "sidebar.html" %} <!--Index-Specific HTML--> {% include "footer.html" %} <!--More Index-Specific HTML--> {% endblock %}
總體而言,與單獨編寫頁面相比,這種結構提供了三個主要好處:

- DRY(不要重複自己)代碼
通過將通用代碼分解到特定文件中,我們可以只在一處更改代碼,並將這些更改反映到所有頁面中。 - 提高可讀性
您可以隔離您感興趣的特定組件,而不是滾動瀏覽一個巨大的文件。 - 關注點分離
比如說,側邊欄的代碼現在必須在一個地方,不能有任何流氓script
標籤漂浮在代碼底部或其他應該是單獨組件之間的混合。 分解出各個部分會強制執行這種良好的編碼實踐。
雖然我們可以通過將特定組件放在base.html模板中來節省更多代碼行,但將它們分開提供兩個優點。 首先是我們能夠將它們準確地嵌入到它們所屬的單個塊中(這僅與位於content
塊的主div
內的footer.html相關)。 另一個優點是,如果我們要創建一個頁面,比如一個 404 錯誤頁面,並且我們不想要側邊欄或頁腳,我們可以將它們排除在外。
這些功能與模板課程相當。 現在,我們轉向可以在index.html中使用的強大標籤來提供動態特性並節省數百行代碼。
兩個基本標籤
這與可用標籤的詳盡列表相去甚遠。 關於模板的 Django 文檔提供了這樣的枚舉。 目前,我們專注於模板語言中兩個最常見元素的用例。 在我自己的工作中,我基本上只定期使用for
和if
標籤,儘管提供的十幾個或更多其他標籤都有自己的用例,我鼓勵您在模板參考中查看。
在我們討論標籤之前,我想對語法做一個註釋。 標籤{% foo %}
表示“foo”是模板系統本身的函數或其他功能,而標籤{{ bar }}
表示“bar”是傳遞給特定模板的變量。
For 循環
在剩下的index.html中,幾百行代碼的最大部分是表格。 我們可以從數據庫動態生成表,而不是這個硬編碼的表。 回想一下設置步驟中的python manage.py loaddata employee_fixture.json
。 該命令使用一個名為 Django Fixture 的 JSON 文件將所有 57 條員工記錄加載到應用程序的數據庫中。 我們使用views.py中的視圖將這些數據傳遞給模板:
from django.shortcuts import render from .models import Employee def index(request): return render(request, "index.html", {"employees": Employee.objects.all()})
render
的第三個位置參數是模板可用的數據字典。 我們使用這些數據和for
標籤來構建表格。 即使在我改編此網頁的原始模板中,員工表也是硬編碼的。 我們的新方法減少了數百行重複的硬編碼表格行。 index.html現在包含:
{% for employee in employees %} <trv <td>{{ employee.name }}</td> <td>{{ employee.position }}</td> <td>{{ employee.office }}</td> <td>{{ employee.age }}</td> vtd>{{ employee.start_date }}</td> <td>${{ employee.salary }}</td> </tr> {% endfor %}
更大的好處是,這大大簡化了更新表的過程。 無需讓開發人員手動編輯 HTML 以反映加薪或新員工,然後將該更改推送到生產中,任何管理員都可以使用管理面板進行實時更新(https://127.0.0.1/admin,使用您使用python manage.py createsuperuser
創建的用於訪問的憑據)。 這是將 Django 與此渲染引擎一起使用而不是在靜態站點生成器或其他模板方法中單獨使用它的好處。
如果別的
if
標籤是一個非常強大的標籤,它允許您評估模板中的表達式並相應地調整 HTML。 像{% if 1 == 2 %}
這樣的行是完全有效的,如果有點無用的話,因為它們每次都評估相同的結果。 if
標記的亮點在於與視圖傳遞到模板的數據進行交互時。 考慮sidebar.html中的以下示例:
<div class="sb-sidenav-footer"> <div class="small"> Logged in as: </div> {% if user.is_authenticated %} {{ user.username }} {% else %} Start Bootstrap {% endif %} </div>
請注意,默認情況下將整個用戶對像傳遞到模板中,而無需我們在視圖中指定任何內容來實現這一點。 這允許我們訪問用戶的身份驗證狀態(或缺少身份驗證狀態)、用戶名和其他功能,包括遵循外鍵關係以訪問存儲在用戶配置文件或其他連接模型中的數據,所有這些都來自 HTML 文件。
您可能會擔心這種訪問級別可能會帶來安全風險。 但是,請記住,這些模板適用於服務器端呈現框架。 構建頁面後,標籤已經消耗了自己,並被純 HTML 替換。 因此,如果if
語句在某些條件下將數據引入頁面,但數據未在給定實例中使用,則該數據根本不會發送到客戶端,因為if
語句是在服務器端評估的。 這意味著正確構建的模板是一種非常安全的方法,可以將敏感數據添加到頁面,除非必要,否則數據不會離開服務器。 也就是說,使用 Django 模板並不能消除以安全、加密方式傳遞敏感信息的需要,它只是意味著像user.is_authenticated
這樣的安全檢查可以安全地在 HTML 中進行,因為它是在服務器端處理的。
此功能還有許多其他用例。 例如,在一般產品主頁中,您可能希望隱藏“註冊”和“登錄”按鈕,並用登錄用戶的“退出”按鈕替換它們。 另一個常見用途是顯示和隱藏表單提交等操作的成功或錯誤消息。 請注意,如果用戶未登錄,通常您不會隱藏整個頁面。根據用戶的身份驗證狀態更改整個網頁的更好方法是在views.py中的適當函數中處理它。
過濾
視圖的部分工作是為頁面適當地格式化數據。 為此,我們對標籤進行了強大的擴展:過濾器。 Django 中有許多過濾器可用於執行諸如對齊文本、格式化日期和添加數字等操作。 基本上,您可以將過濾器視為應用於標籤中變量的函數。 例如,我們希望我們的工資數字顯示為“$1,200,000”而不是“1200,000”。 我們將使用過濾器在index.html中完成工作:
<td>${{ employee.salary|intcomma }}</td>
管道字符|
是將intcomma
命令應用於employee.salary
變量的過濾器。 “$”字符不是來自模板,對於像這樣每次出現的元素,將其粘貼在標籤之外更容易。
請注意, intcomma
要求我們在settings.py的INSTALLED_APPS
中的index.html和'django.contrib.humanize',
的頂部包含{% load humanize %}
。 這是在提供的示例應用程序中為您完成的。
結論
使用 Jinja2 引擎的服務器端渲染為創建乾淨、適應性強、響應迅速的前端代碼提供了關鍵工具。 將頁面分成文件允許具有靈活組合的 DRY 組件。 標籤提供了顯示視圖函數從數據庫傳遞的數據的基本功能。 如果做得好,這種方法可以提高網站的速度、SEO 功能、安全性和可用性,並且是 Django 和類似框架編程的核心方面。
如果您還沒有這樣做,請查看示例應用程序並嘗試使用完整列表添加您自己的標籤和過濾器。
Django Highlights 是一個介紹 Django 中 Web 開發的重要概念的系列。 每篇文章都是作為 Django 開發方面的獨立指南編寫的,旨在幫助前端開發人員和設計人員更深入地了解代碼庫的“另一半”。 這些文章主要是為了幫助您了解理論和約定,但包含一些代碼示例,這些示例是用 Django 3.0 編寫的。
該系列的先前部分:
- Django 亮點:用戶模型和身份驗證(第 1 部分)
- Django 亮點:模型、管理和利用關係數據庫(第 3 部分)
- Django 亮點:處理靜態資產和媒體文件(第 4 部分)