在上一章節中我們使用 django.http.HttpResponse() 來輸出 "Hello World!"。該方式将資料與視圖混合在一起,不符合 Django 的 MVC 思想。
本章節我們将為大家詳細介紹 Django 模闆的應用,模闆是一個文本,用于分離文檔的表現形式和内容。
我們接着上一章節的項目将在 HelloWorld 目錄底下建立 templates 目錄并建立 runoob.html檔案,整個目錄結構如下:
runoob.html 檔案代碼如下:
<h1>{{ hello }}</h1>
從模闆中我們知道變量使用了雙括号。
接下來我們需要向Django說明模闆檔案的路徑,修改HelloWorld/settings.py,修改 TEMPLATES 中的 DIRS 為 <b>[os.path.join(BASE_DIR, 'templates')]</b>,如下所示:
...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(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',
],
},
},
]
我們現在修改 views.py,增加一個新的對象,用于向模闆送出資料:
from django.shortcuts import render
def runoob(request):
context = {}
context['hello'] = 'Hello World!'
return render(request, 'runoob.html', context)
from django.urls import path
from . import views
urlpatterns = [
path('runoob/', views.runoob),
可以看到,我們這裡使用 render 來替代之前使用的 HttpResponse。render 還使用了一個字典 context 作為參數。
context 字典中元素的鍵值 hello 對應了模闆中的變量 {{ hello }}。
再次通路 http://127.0.0.1:8000/runoob,可以看到頁面:

這樣我們就完成了使用模闆來輸出資料,進而實作資料與視圖分離。
接下來我們将具體介紹模闆中常用的文法規則。
模闆文法:
views_name = "菜鳥教程"
return render(request,"runoob.html", {"name":views_name})
templates 中的 runoob.html :
templates 中的 runoob.html中,可以用 . 索引下标取出對應的元素。
views_list = ["菜鳥教程1","菜鳥教程2","菜鳥教程3"]
return render(request, "runoob.html", {"views_list": views_list})
<p>{{ views_list }}</p> # 取出整個清單
<p>{{ views_list.0 }}</p> # 取出清單的第一個元素
templates 中的 runoob.html中,可以用 .鍵 取出對應的值。
views_dict = {"name":"菜鳥教程"}
return render(request, "runoob.html", {"views_dict": views_dict})
<p>{{ views_dict }}</p>
<p>{{ views_dict.name }}</p>
模闆過濾器可以在變量被顯示前修改它,過濾器使用管道字元,如下所示:
{{ name }} 變量被過濾器 lower 處理後,文檔大寫轉換文本為小寫。
過濾管道可以被* 套接* ,既是說,一個過濾器管道的輸出又可以作為下一個管道的輸入:
以上執行個體将第一個元素并将其轉化為大寫。
有些過濾器有參數。 過濾器的參數跟随冒号之後并且總是以雙引号包含。 例如:
這個将顯示變量 bio 的前30個詞。
其他過濾器:
addslashes : 添加反斜杠到任何反斜杠、單引号或者雙引号前面。
date : 按指定的格式字元串參數格式化 date 或者 datetime 對象,執行個體:
length : 傳回變量的長度。
default
default 為變量提供一個預設值。
如果 views 傳的變量的布爾值是 false,則使用指定的預設值。
以下值為 false:
name =0
return render(request, "runoob.html", {"name": name})
{{ name|default:"菜鳥教程666" }}
length
傳回對象的長度,适用于字元串和清單。
字典傳回的是鍵值對的數量,集合傳回的是去重後的長度。
name ="菜鳥教程"
{{ name|length}}
filesizeformat
以更易讀的方式顯示檔案的大小(即'13 KB', '4.1 MB', '102 bytes'等)。
num=1024
return render(request, "runoob.html", {"num": num})
{{ num|filesizeformat}}
date
根據給定格式對一個日期變量進行格式化。
格式 Y-m-d H:i:s傳回 年-月-日 小時:分鐘:秒 的格式時間。
import datetime
now =datetime.datetime.now()
return render(request, "runoob.html", {"time": now})
{{ time|date:"Y-m-d" }}
truncatechars
如果字元串包含的字元總個數多于指定的字元數量,那麼會被截斷掉後面的部分。
截斷的字元串将以 ... 結尾。
views_str = "菜鳥教程"
return render(request, "runoob.html", {"views_str": views_str})
{{ views_str|truncatechars:2}}
再通路通路 http://127.0.0.1:8000/runoob,可以看到頁面:
safe
将字元串标記為安全,不需要轉義。
要保證 views.py 傳過來的資料絕對安全,才能用 safe。
和後端 views.py 的 mark_safe 效果相同。
Django 會自動對 views.py 傳到HTML檔案中的标簽文法進行轉義,令其語義失效。加 safe 過濾器是告訴 Django 該資料是安全的,不必對其進行轉義,可以讓該資料語義生效。
views_str = "<a href='https://www.runoob.com/'>點選跳轉</a>"
{{ views_str|safe }}
基本文法格式如下:
或者:
根據條件判斷是否輸出。if/else 支援嵌套。
{% if %} 标簽接受 and , or 或者 not 關鍵字來對多個變量做判斷 ,或者對變量取反( not ),例如:
views_num = 88
return render(request, "runoob.html", {"num": views_num})
{%if num > 90 and num <= 100 %}
優秀
{% elif num > 60 and num <= 90 %}
合格
{% else %}
一邊玩去~
{% endif %}
{% for %} 允許我們在一個序列上疊代。
與 Python 的 for 語句的情形類似,循環文法是 for X in Y ,Y 是要疊代的序列而 X 是在每一個特定的循環中使用的變量名稱。
每一次循環中,模闆系統會渲染在 {% for %} 和 {% endfor %} 之間的所有内容。
例如,給定一個運動員清單 athlete_list 變量,我們可以使用下面的代碼來顯示這個清單:
views_list = ["菜鳥教程","菜鳥教程1","菜鳥教程2","菜鳥教程3",]
{% for i in views_list %}
{{ i }}
{% endfor %}
給标簽增加一個 reversed 使得該清單被反向疊代:
{% for i in views_list reversed%}
周遊字典: 可以直接用字典 .items 方法,用變量的解包分别擷取鍵和值。
views_dict = {"name":"菜鳥教程","age":18}
{% for i,j in views_dict.items %}
{{ i }}---{{ j }}
在 {% for %} 标簽裡可以通過 {{forloop}} 變量擷取循環序号。
forloop.counter: 順序擷取循環序号,從 1 開始計算
forloop.counter0: 順序擷取循環序号,從 0 開始計算
forloop.revcounter: 倒叙擷取循環序号,結尾序号為 1
forloop.revcounter0: 倒叙擷取循環序号,結尾序号為 0
forloop.first(一般配合if标簽使用): 第一條資料傳回 True,其他資料傳回 False
forloop.last(一般配合if标簽使用): 最後一條資料傳回 True,其他資料傳回 False
views_list = ["a", "b", "c", "d", "e"]
return render(request, "runoob.html", {"listvar": views_list})
{% for i in listvar %}
{{ forloop.counter }}
{{ forloop.counter0 }}
{{ forloop.revcounter }}
{{ forloop.revcounter0 }}
{{ forloop.first }}
{{ forloop.last }}
{% empty %}
可選的 {% empty %} 從句:在循環為空的時候執行(即 in 後面的參數布爾值為 False )。
views_list = []
return render(request, "runoob.html", {"listvar": views_list})
空空如也~
可以嵌套使用 {% for %} 标簽:
{% ifequal %} 标簽比較兩個值,當他們相等時,顯示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。
下面的例子比較兩個模闆變量 user 和 currentuser :
和 {% if %} 類似, {% ifequal %} 支援可選的 {% else%} 标簽:8
Django 注釋使用 {# #}。
{% include %} 标簽允許在模闆中包含其它的模闆的内容。
下面這個例子都包含了 nav.html 模闆:
csrf_token 用于form表單中,作用是跨站請求僞造保護。
如果不用{% csrf_token %}标簽,在用 form 表單時,要再次跳轉頁面會報403權限錯誤。
用了{% csrf_token %}标簽,在 form 表單送出資料時,才會成功。
解析:
首先,向伺服器發送請求,擷取登入頁面,此時中間件 csrf 會自動生成一個隐藏input标簽,該标簽裡的 value 屬性的值是一個随機的字元串,使用者擷取到登入頁面的同時也擷取到了這個隐藏的input标簽。
然後,等使用者需要用到form表單送出資料的時候,會攜帶這個 input 标簽一起送出給中間件 csrf,原因是 form 表單送出資料時,會包括所有的 input 标簽,中間件 csrf 接收到資料時,會判斷,這個随機字元串是不是第一次它發給使用者的那個,如果是,則資料送出成功,如果不是,則傳回403權限錯誤。
1、在應用目錄下建立 templatetags 目錄(與 templates 目錄同級,目錄名隻能是 templatetags)。
2、在 templatetags 目錄下建立任意 py 檔案,如:my_tags.py。
3、my_tags.py 檔案代碼如下:
修改 settings.py 檔案的 TEMPLATES 選項配置,添加 libraries 配置:
'DIRS': [BASE_DIR, "/templates",],
"libraries":{ # 添加這邊三行配置
'my_tags':'templatetags.my_tags' # 添加這邊三行配置
} # 添加這邊三行配置
4、利用裝飾器 @register.filter 自定義過濾器。
注意:裝飾器的參數最多隻能有 2 個。
5、利用裝飾器 @register.simple_tag 自定義标簽。
6、在使用自定義标簽和過濾器前,要在 html 檔案 body 的最上方中導入該 py 檔案。
7、在 HTML 中使用自定義過濾器。
8、在 HTML 中使用自定義标簽。
9、語義化标簽
在該 py 檔案中導入 mark_safe。
定義标簽時,用上 mark_safe 方法,令标簽語義化,相當于 jQuery 中的 html() 方法。
和前端HTML檔案中的過濾器 safe 效果一樣。
在HTML中使用該自定義标簽,在頁面中動态建立标簽。
1、在項目根目錄下建立 statics 目錄。
2、在 settings 檔案的最下方配置添加以下配置:
3、在 statics 目錄下建立 css 目錄,js 目錄,images 目錄,plugins 目錄, 分别放 css檔案,js檔案,圖檔,插件。
4、把 bootstrap 架構放入插件目錄 plugins。
5、在 HTML 檔案的 head 标簽中引入 bootstrap。
注意:此時引用路徑中的要用配置檔案中的别名 static,而不是目錄 statics。
在模闆中使用需要加入 {% load static %} 代碼,以下執行個體我們從靜态目錄中引入圖檔。
{% load static %}
{{name}}<img src="{% static "images/runoob-logo.png" %}" alt="runoob-logo">
模闆可以用繼承的方式來實作複用,減少備援内容。
網頁的頭部和尾部内容一般都是一緻的,我們就可以通過模闆繼承來實作複用。
父模闆用于放置可重複利用的内容,子模闆繼承父模闆的内容,并放置自己的内容。
标簽 block...endblock: 父模闆中的預留區域,該區域留給子模闆填充差異性的内容,不同預留區域名字不能相同。
子模闆使用标簽 extends 繼承父模闆:
子模闆如果沒有設定父模闆預留區域的内容,則使用在父模闆設定的預設内容,當然也可以都不設定,就為空。
子模闆設定父模闆預留區域的内容:
接下來我們先建立之前項目的 templates 目錄中添加 base.html 檔案,代碼如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鳥教程(runoob.com)</title>
</head>
<body>
<h1>Hello World!</h1>
<p>菜鳥教程 Django 測試。</p>
{% block mainbody %}
<p>original</p>
{% endblock %}
</body>
</html>
以上代碼中,名為 mainbody 的 block 标簽是可以被繼承者們替換掉的部分。
所有的 {% block %} 标簽告訴模闆引擎,子模闆可以重載這些部分。
runoob.html 中繼承 base.html,并替換特定 block,runoob.html 修改後的代碼如下:
{%extends "base.html" %}
<p>繼承了 base.html 檔案</p>
第一行代碼說明 runoob.html 繼承了 base.html 檔案。可以看到,這裡相同名字的 block 标簽用以替換 base.html 的相應 block。
重新通路位址 http://127.0.0.1:8000/runoob,輸出結果如下: