8、數據渲染與顯示
1 概述
- Django作為Web框架,需要一種很便利的方法動態地生成HTML網頁,因此有了模板這個概念。模板包含所需HTML的部分代碼以及一些特殊語法,特殊語法用于描述如何將視圖傳遞的數據動態插入HTML網頁中。
- Django可以配置一個或多個模板引擎(甚至是0個,如前后端分離,Django只提供API,無須使用模板引擎),模板引擎有 Django 模板語言(Django Template Language,DTL)和Jinja2。Django 模板語言是Django內置的功能之一,它包含模板上下文(也可稱為模板變量)標簽和過濾器,各個功能說明如下:
- 模板上下文是以變量的形式寫入模板文件中的,變量值由視圖函數或視圖類傳遞所得。
- 標簽是對模板上下文進行控制輸出,比如模板上下文的判斷和循環控制等。
- 模板繼承隸屬于標簽,它是將每個模板文件重復的代碼抽取出來并寫在一個共用的模板文件中,其他模板文件通過繼承共用模板文件來實現完整的網頁輸出。
- 過濾器是對模板上下文進行操作處理,比如模板上下文的內容截取、替換或格式轉換等。
2 模板上下文
-
模板上下文(也可以稱為模板變量)是模板中基本的組成單位,上下文的數據由視圖函數或視圖類傳遞。它以 { { variable }} 表示,variable是上下文的名稱,它支持Python所有的數據類型,如字典、列表、元組、字符串、整型或實例化對象等。上下文的數據格式不同,在模板中的使用方式也有所差異,示例代碼如下:
#假如 variablel = '字符串或整型' <div>{{ variablel}}</div> # 輸出 "<div>字符串或整型</div>"# 假如 variable2 = {'name' : '字典或實例化對象'} <div>{{ variable2.name }}</div> #輸出 "<div>字典或實例化對象</div>"#假如 variable3 = ['元組或列表'] <div>{{ variable3.0 }}</div> #輸出 "<div>元組或列表</div>"
-
從上述代碼發現,如果上下文的數據帶有屬性,就可以在上下文的末端使用
“.”
來獲取某個屬性的值。比如上下文為字典或實例化對象,在上下文末端使用“.”
并寫入屬性名稱即可在網頁上顯示該屬性的值;若上下文為元組或列表,則在上下文末端使用 “”并設置索引下標來獲取元組或列表的某個元素值。 -
如果視圖沒有為模板上下文傳
”
遞數據或者模板上下文的某個屬性、索引下標不存在,Django 就會將其設為空值。例如獲取 variable2的屬性age,由于上述的variable2并不存在屬性age,因此網頁上將會顯示 “ -
在PyCharm的Debug調試模式下分析Django模板引擎的運行過程。打開函數render所在的源碼文件,變量content是模板文件的解析結果,它是由函數render_to_string完成解析過程的如圖6-6所示。
def render(request, template_name, context=None, content_type=None, status=None, using=None):"""Return an HttpResponse whose content is filled with the result of callingdjango.template.loader.render_to_string() with the passed arguments."""content = loader.render_to_string(template_name, context, request, using=using)return HttpResponse(content, content_type, status)
-
想要分析Diango模板引擎的解析過程,還需要從函數 render_to_string 深入分析,通過PyCharm打開函數 render_to_string 的源碼信息,發現它調用了函數 get_template 或 select_template,模板的解析過程:
3 內置標簽
-
標簽是對模板上下文進行控制輸出,它是以 {% tag %} 表示的,其中tag是標簽的名稱,Django內置了許多模板標簽,比如{% if %}(判斷標簽)、{% for %}(循環標簽)或{% url %)(路由標簽)等。
-
內置的模板標簽可以在Django源碼(\django\template\defaulttags.py)中找到定義過程,每個內置標簽都有功能注釋和使用方法,本書只列舉常用的內置標簽:
標簽名稱 語法 功能描述 示例 if
{% if condition %} ... {% elif condition %} ... {% else %} ... {% endif %}
條件判斷,支持 and
,or
,not
,==
,!=
,>
,<
,>=
,<=
等運算符{% if user.is_staff %} Admin Panel {% else %} User Dashboard {% endif %}
for
{% for item in list %} ... {% empty %} ... {% endfor %}
迭代列表、查詢集等可迭代對象,支持 forloop.counter
,forloop.revcounter
等變量{% for comment in post.comments %} { { comment.text }} {% empty %} No comments {% endfor %}
csrf_token
{% csrf_token %}
生成 CSRF 保護令牌,用于 POST 請求表單防跨站攻擊 <form method="post">{% csrf_token %} <button>Submit</button> </form>
url
{% url 'view_name' arg1 arg2 %} 或 {% url 'app_name:view_name' %}
反向解析 URL 路徑,避免硬編碼 URL {% url 'blog:article_detail' article.slug %}
load
{% load static i18n %}
加載自定義或內置模板標簽庫(如 static
,humanize
,i18n
等){% load static %}
或{% load custom_tags %}
static
{% static 'path/to/file.css' %}
生成靜態文件的 URL 路徑(需先 {% load static %}
)<link href="{% static 'css/style.css' %}" rel="stylesheet">
extends
{% extends "base.html" %}
聲明模板繼承關系,必須放在模板文件頂部 {% extends "layouts/main.html" %}
block
{% block block_name %} ... {% endblock %}
定義可被子模板覆蓋的內容區塊,常用于模板繼承 {% block content %} Main content here {% endblock %}
- 關鍵說明:
- 模板繼承順序:
extends
標簽必須位于模板文件的第一行 - CSRF 保護:所有 POST 表單必須包含
csrf_token
標簽 - 靜態文件路徑:
static
標簽會自動添加STATIC_URL
設置前綴 - URL 命名空間:推薦使用
app_name:view_name
格式避免命名沖突 - 循環變量:
for
標簽內置forloop
對象包含counter
,counter0
,first
,last
等屬性
- 模板繼承順序:
- 關鍵說明:
-
for 標簽模板變量
變量名稱 描述 示例用法 forloop.counter
當前循環的迭代次數(從 1 開始) { { forloop.counter }}
輸出:1, 2, 3…forloop.counter0
當前循環的迭代次數(從 0 開始) { { forloop.counter0 }}
輸出:0, 1, 2…forloop.revcounter
剩余迭代次數(從總數開始遞減) 若總迭代 3 次,輸出:3, 2, 1 forloop.revcounter0
剩余迭代次數(從總數 - 1 開始遞減) 若總迭代 3 次,輸出:2, 1, 0 forloop.first
當前是否為第一次迭代(布爾值) {% if forloop.first %} First item {% endif %}
forloop.last
當前是否為最后一次迭代(布爾值) {% if forloop.last %} Last item {% endif %}
forloop.parentloop
嵌套循環中引用父級循環的 forloop
對象內層循環中使用 { { forloop.parentloop.counter }}
4 模板繼承
-
模板繼承是通過模板標簽來實現的,其作用是將多個模板文件的共同代碼集中在一個新的模板文件中,然后各個模板可以直接調用新的模板文件,從而生成HTML網頁,這樣可以減少模板之間重復的代碼。
{% extends 'base.html' %} {% block title %} <title>首頁</title> {% endblock %}{% block body %}<a href="{% url 'index:index' %}">首頁</a><h1>Hello,Django</h1> {% endblock %}
-
模板的繼承與Python的類繼承原理是一致的,通過繼承方式使得其具有父類的功能和屬性,同時也可以通過重寫來實現更加復雜的開發需求。
5 內置過濾器
-
過濾器主要是對上下文的內容進行操作,如:替換、反序和轉義等。通過過濾器處理上下文可以將其數據格式或內容轉化為我們想要的顯示效果,而且可以相應地減少視圖的代碼量。過濾器的使用方法如下:
- { { variable | filter }}
-
若上下文設有過濾器,則模板引擎在解析上下文時,首先由過濾器 filter 處理上下文variable,然后對處理后的結果進行解析并顯示在網頁上。variable代表模板上下文,管道符號 “|” 代表當前上下文使用過濾器,filter代表某個過濾器。單個上下文可以支持多個過濾器同時使用,例如:
- { { variable | filter | lower})
-
在使用的過程中,有些過濾器還可以傳入參數,但僅支持傳入一個參數。帶參數的過濾器與參數之間使用冒號隔開,并且兩者之間不能留有空格,例如:
- { { variable | date:“D d M Y”}}
-
Django的內置過濾器可以在源碼(\django\template\defaultfilters.py) 中找到具體的定義過程:
過濾器名稱 語法 功能描述 示例 length
`value length` 返回字符串或列表的長度 default
`value default:“N/A”` 若值為假(空字符串、None 等),返回默認值 date
`date_obj date:“Y-m-d”` 格式化日期時間對象,支持 Y
,m
,d
,H
,i
,s
,N
等格式代碼upper
/lower
`text upper` 將字符串轉換為大寫 / 小寫 truncatewords
`text truncatewords:3` 截斷字符串為指定單詞數,超出部分用省略號表示 safe
`html safe` 標記字符串為安全 HTML,避免自動轉義 add
`num add:5` 數值相加(或字符串拼接) join
`list join:", "` 類似 Python 的 join()
方法,將列表元素連接為字符串filesizeformat
`bytes filesizeformat` 將字節數格式化為人類可讀的文件大小(KB、MB 等) slice
`list slice:“:2”` 切片操作,類似 Python 的 [:2]
linebreaks
`text linebreaks` 將換行符轉換為 HTML 的 <br>
或<p>
標簽yesno
`bool yesno:“Y,N,?”` 將布爾值轉換為自定義文本(是 / 否 / 未知) divisibleby
`num divisibleby:2` 判斷數值是否能被整除(返回布爾值) floatformat
`num floatformat:2` 格式化浮點數,指定小數位數(四舍五入) urlencode
`text urlencode` 將字符串編碼為 URL 安全格式 -
補充說明:
- 鏈式過濾器:可組合使用多個過濾器,如
{ { value|lower|truncatewords:5 }}
- 安全注意:使用
safe
過濾器時需確保內容來源安全,避免 XSS 攻擊 - 自定義過濾器:可通過
{% load %}
標簽加載自定義過濾器庫
- 鏈式過濾器:可組合使用多個過濾器,如
-
注意
- 使用過濾器過程中,上下文、管道符號 “|” 和 過濾器之間沒有規定使用空格隔開。為了符合編碼的規范性,建議使用空格隔開。
- 若過濾器需要設置參數,過濾器、冒號和參數之間不能有供果,否則會提示:TemplateSyntaxError
6 自定義異常頁面
-
網站異常是一個普遍現象,常見的異常以 404 和 500 為主,出現異常的網站自身數據缺陷或不合理的非法訪問導致,
-
實現自定義異常頁面,操作如下:
-
在你的項目模板目錄下(通常是
templates/
)創建以下兩個模板文件:-
404.html(處理未找到頁面)
-
500.html(處理服務器內部錯誤)
<!-- templates/404.html --> <!DOCTYPE html> <html> <head><title>404 - 頁面未找到</title> </head> <body><h1>頁面不存在</h1><p>你請求的頁面不存在,請檢查URL或返回<a href="/">首頁</a>。</p> </body> </html><!-- templates/500.html --> <!DOCTYPE html> <html> <head><title>500 - 服務器錯誤</title> </head> <body><h1>服務器開小差了</h1><p>我們正在修復問題,請稍后再試。</p> </body> </html>
-
-
配置 URL 處理函數(可選)
-
Django 默認會自動處理 404 和 500 錯誤,但如果你需要自定義處理邏輯,可以在項目的
urls.py
中添加:# project/urls.py from django.conf.urls import handler404, handler500 from django.contrib import admin from django.urls import pathurlpatterns = [path('admin/', admin.site.urls),# 其他URL配置... ]# 指定錯誤處理視圖 handler404 = 'your_app.views.custom_404' handler500 = 'your_app.views.custom_500'
-
-
創建自定義視圖函數(可選)
-
如果你在第 2 步中指定了自定義處理函數,需要在對應應用的
views.py
中實現:# your_app/views.py from django.shortcuts import renderdef custom_404(request, exception):return render(request, '404.html', status=404)def custom_500(request):return render(request, '500.html', status=500)
-
-
-
注意
- DEBUG 模式:僅在
DEBUG=False
時才會顯示自定義錯誤頁面 - 500 錯誤處理:
handler500
不接收exception
參數,因為服務器錯誤可能無法提供具體異常信息 - 靜態文件:確保錯誤頁面不依賴動態數據,避免在出錯時引發更多問題
- DEBUG 模式:僅在
7 基礎模板設計
-
從網頁靜態界面看到,所有網頁(除了登錄頁外)的頂部都是相同的,包含商品搜索功能和網站導航
-
由于每個網頁的頂部都相同,也就是說這部分的HTML代碼是完全相同的,因此我們可以將這部分代碼單獨抽取出來并放置在一