一般而言,我們在視圖函數中處理各種業務邏輯之后,應該返回一個 HttpResponse 對象。而 HttpResponse 對象的第一個參數接受字符串或者是迭代器,作為響應報文的主體。但是這意味着我們要將 HTML 內容寫到 python 的代碼中,或者寫到一個文件中,然后用 python 打開,將可迭代的文件對象作為第一個參數。
這樣的后果是:
1、代碼很混亂,而且實際工作中前后端一般是由不同的程序員處理的,讓可能不懂 python 的前端人員在 python 文件中書寫 HTML 代碼是一件很危險並且效率低下的事情。
2、內容很難做到動態改變,這就失去的動態生成頁面的優勢了。
為了解決以上的問題,django引入了模板層,當然這也是所有遵循MVC框架都有的。在1.8版本之前,django只支持內置的django模板引擎,簡稱為 DTL;在1.8以后,django還加入了對 jinja2 的支持,但是,如果你要使用jinja2模板引擎的話,還需要自己去下載jinja2的庫( pip install Jinja2 ),因為django只提供了接口的支持。另外,如何你還有使用其他的第三方引擎,可能要進行 后端(BACKEND)的自定義,因為django只提供了 DTL 和jinja2的后端支持。
接下來來看看如何配置使用。
模板的配置是通過 setting.py 中的 TEMPLATES 變量進行的,下面是通過 startproject 命令生成的項目中(1.9版本),在 setting 中的默認配置:
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', ], }, }, ]
首先,TEMPLATES 是一個列表,列表中的每個元素都是一個字典,每個字典代表一個模板引擎。上面的就是默認的配置,代表只使用 django 自帶的 DTL 模板引擎,當然如果你有需要同時使用多個模板引擎的話,可以這樣配置:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ '/home/html/example.com', '/home/html/default', ], }, { 'BACKEND': 'django.template.backends.jinja2.Jinja2', 'DIRS': [ '/home/html/jinja2', ], }, ]
這樣就可以根據需要使用不同的引擎了,一般通過函數的 using 參數來指定引擎的 NAME。關於這兩個參數的含義下面會進行深入的解釋。
接下來看看要怎么配置:
TEMPLATES:默認是一個空列表,列表中的每個元素都是字典,每個字典代表一個模板引擎的配置。一般我們使用 startproject 命令來生成項目時,這里都會自動填充為第一個例子所示的內容,當然因版本的不同會有差異。如果你不打算使用模板的話,也可以把里面的元素刪掉,只留一個空列表。
BACKEND:后端,是字典中的一個鍵,當然接下來的所有設置都位於字典中,下面就不一一說明強調了。沒有默認值。這里應該是一個字符串,字符串的內容是可調用的 python 路徑,也就是說如果在 python 中 import 的時候,應該能夠找到。django支持的后端都位於 django.template.backends 目錄下,例如:
它們是模塊中的一個類,這個類繼承於 BaseEngine :
更深層次的東西這里就不繼續討論了。
NAME:模板引擎的別名,通常會在各種函數的 using 中用到。默認為定義后端(backends)的模塊的名稱,由上圖可知,django的模板引擎的別名就是‘django’;jiaja2模板引擎的別名就是‘jinja2’。
DIRS:搜索模板的路徑順序,模板引擎將按照里面的順序來搜索模板文件。默認為空列表。注意:這里的路徑應該是django可以訪問的路徑,因為我們的服務器一般運行在linux平台中,而linux平台對於權限的管理非常嚴格。一般我們的django的通過 wsgi 運行在 web 服務器后面時,如Apache、nginx等,服務器的權限是什么,django的權限就是什么。
APP_DIRS:默認為False,但是 startproject 命令會為我們設置為 True。告訴模板引擎是否應該進入每個已安裝的應用中查找模板。每種模板引擎后端都定義了一個固定的名稱作為應用內存放模板的子目錄的名稱。例如,django的就叫做‘templates’ ;jinja2的就是‘jinja2’。
OPTIONS:傳遞額外的參數給后端引擎,表示開啟某些功能。可用的參數隨着模板引擎的不同而不同。默認為一個空字典,當然 startproject 命令為我們填充了一些參數。
下面是一些舊版本中的特性,在1.8之前,並不是所以的設置都在 TEMPLATES 變量中的,通常會分開設置:
TEMPLATE_CONTEXT_PROCESSORS:
默認:
["django.contrib.auth.context_processors.auth", "django.template.context_processors.debug", "django.template.context_processors.i18n", "django.template.context_processors.media", "django.template.context_processors.static", "django.template.context_processors.tz", "django.contrib.messages.context_processors.messages"]
在1.8中是一個元祖,在1.9中是一個列表,不過里面的內容是一樣的。在1.8版本中已經廢棄了,現在應該在 OPTIONS 中,使用 'context_processors'
來代替,就像文章一開始的那個例子那樣。當然這里針對的是 django 模板引擎。另外,在1.8版本開始,內置的模板上下文處理器從 django.core.context_processors 移動到 django.template.context_processors 中。
這是一個列表,列表里面的給個元素應該是一個字符串,字符串的內容是一個可調用的對象,它們被用來在請求上下文(RequestContext)中填充特定的內容。這些可調用的對象將獲得一個 request 對象作為參數,同時返回一個含有成員的字典,這個字典中的成員將被用來填充內容。
下面是其中的一個函數:
def static(request): """ Adds static-related context variables to the context. """ return {'STATIC_URL': settings.STATIC_URL}
TEMPLATE_DEBUG:
默認:False
在1.8版本開始拋棄,改為在 OPTIONS 字典中設置
'debug'
鍵及其值。
這是一個布爾值,表示是否開啟模板的 debug 模式。如果為 True ,那么在模板渲染的時候觸發任何異常時,將顯示一個詳細的錯誤頁面,錯誤頁面中包含模板的相關代碼,並會用適當的方式強度錯誤的地方。
只有在為 True 的時候才會顯示這個頁面,所以在開發階段可以開啟以便調試。
另外,DEBUG 設置也有類似的功能。
TEMPLATE_DIRS
在1.9中默認為一個空列表,在1.8中為一個空元祖。
這是一個表示模板文件的路徑的容器,當你調用 django.template.loaders.filesystem.Loader 函數時,將會按照里面的順序搜索模板文件。
注意,這些路徑應該使用Unix風格的正斜杠(/),即使在Windows中運行。
當然,在1.8以后,由 DIRS 代替了,也就是開頭的幾個例子中的那樣。
TEMPLATE_LOADERS
默認:
['django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader']
在1.9中是一個列表,在1.8中是一個元祖,里面的內容倒是沒有變。
另外,在1.8版本以后,應該在 OPTIONS 字典中使用'loaders' 鍵作為代替。
容器中的每個元素應該是個字符串,每個字符串是一個可調用的python路徑,所指代的是用於加載模板所使用的類。每個類都知道如何從特定源導入模板。另外,可以使用一個元祖來代替字符串,元祖中的第一個元素是 Loader 所在的模塊,剩下的元素傳遞給 Loader 用於初始化的。
注意:這里可以看出 DIRS 中的路徑查找比 app 目錄中的優先。
TEMPLATE_STRING_IF_INVALID
默認為:''(空字符串)
在1.8版本之后,應該在 OPTIONS 字典中使用 'string_if_invalid' 鍵作為替代。
當模板中使用的變量是無效的(例如拼寫錯誤)時,用來代替輸出的字符串。
'file_charset '
是 OPTIONS 字典中的一個鍵,其值表示從磁盤中讀取模板文件時所用的字符集,默認為 FILE_CHARSET 的值(‘utf-8’),而 FILE_CHARSET 是管理從磁盤中打開文件時所用的字符集,包括模板文件和初始SQL數據文件。
下面兩個是1.9中新增的:
'libraries'
它的值是一個字典,字典中包含了標簽和模板模塊路徑,路徑用Python的點路徑表示,用於在模板引擎中注冊它們。這可以用來添加新的庫或為現有的庫提供備用標簽。
例如:
OPTIONS={ # 其他設置 'libraries': { 'myapp_tags': 'path.to.myapp.tags', 'admin.urls': 'django.contrib.admin.templatetags.admin_urls', }, # 其他設置 }
可以通過將相應的字典鍵傳遞到 {% load %}
標簽,以加載庫。例如: {% load 'myapp_tags' %}
'builtins'
一個列表,列表中的元素是一個python點路徑的字符串,用來從模板標簽模塊中添加 built-ins(內建)的標簽。
例如:
OPTIONS={ 'builtins': ['myapp.builtins'], }
內建的標簽和過濾器可以在不調用{% load %}
標簽的情況下調用。
加載模板
django.template.loader 定義了兩個函數以加載模板
1. get_template(template_name[, dirs][, using])
該函數使用給定的名稱加載模板並返回一個Template 對象.(該名稱是字符串類型,可以是文件名稱,也可以是包含路徑分隔符 / 的名稱)
所獲得的返回值並不是 django.template.Template 的實例,而是后端模板引擎所定義的 Template 對象。
get_template()會嘗試獲取從所有的路徑中獲取模板。如果模板不能成功找到,將會拋出 TemplateDoesNotExist. 如果能夠找到模板但是包含非法值,將會拋出 TemplateSyntaxError.
模板的查找和加載機制取決於每種后台引擎和配置。
template_name:模板的名稱,django會根據配置用這個名字去搜索指定的模板。
例如,當配置為下面所示時:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ '/home/html/example.com', '/home/html/default', ], }, { 'BACKEND': 'django.template.backends.jinja2.Jinja2', 'DIRS': [ '/home/html/jinja2', ], }, ]
如果你調用函數get_template('story_detail.html'), Django按以下順序查找story_detail.html:
- /home/html/example.com/story_detail.html ('django' engine)
- /home/html/default/story_detail.html ('django' engine)
- /home/html/jinja2/story_detail.html ('jinja2' engine)
會先用第一個引擎,然后再使用第二個引擎的配置進行搜索,當找到第一個符合的文件時,就會停止搜索,也就是后面也有同名的文件的話,將會‘失明’。
另外,我們可以使用包括路徑的名稱,django也推薦這樣做,最好的方法就是為每一個app建立一個同名的目錄,然后在該目錄下放置這個app要用到的模板,這樣可以避免重名問題帶來的模板查找錯誤,例如: get_template('news/story_detail.html')
- /home/html/example.com/news/story_detail.html ('django' engine)
- /home/html/default/news/story_detail.html ('django' engine)
- /home/html/jinja2/news/story_detail.html ('jinja2' engine)
dirs :該參數從1.8起被移除,這里不再討論。
using:表示要使用哪個引擎進行查找,該參數接受的是模板引擎的 NAME ,詳情參考上面的配置中的 NAME。默認為 None ,如上面的例子所示,為 None 時將使用兩個引擎進行查找,兩個引擎按配置的先后進行查找,如果指定了使用某個引擎,另一個引擎就不會再查找了。
2. select_template(template_name_list[, dirs][, using])
select_template() 和 get_template()很相似, 只不過它用了一個模板名稱的列表作為參數。按順序搜索模板名稱列表內的模板並返回第一個存在的模板。
例如:如果你調用函數 select_template(['story_253_detail.html', 'story_detail.html']), Django按以下順序查找:
- /home/html/example.com/story_253_detail.html ('django' engine)
- /home/html/default/story_253_detail.html ('django' engine)
- /home/html/jinja2/story_253_detail.html ('jinja2' engine)
- /home/html/example.com/story_detail.html ('django' engine)
- /home/html/default/story_detail.html ('django' engine)
- /home/html/jinja2/story_detail.html ('jinja2' engine)
其他的參數都是一樣的,這里不重復說明。
你可以通過 select_template() 來實現更為靈活的模板加載。例如: select_template(['story_%s_detail.html' %story.id, 'story_detail.html'])的形式。
渲染
按照約定,查找到的模板,也就是調用上面兩個搜索模板的函數后,將返回一個由后端模板引擎所定義的 Template 對象,該對象必須實現 render()方法,所以在自定義模板引擎的時候要要注意這一點。
Template.render(context=None, request=None)
context:它必須是一個python字典對象(dict),將用這個字典對模板進行渲染,如果沒有提供,將使用一個空的字典。
request:它必須是一個 Httprequest 對象,后端引擎必須妥善處理它,同時還要處理 CSRF 令牌,當然如何實現是后端引擎做的事,也取決於不同的后端引擎。
render_to_string(template_name[, context][, context_instance][, request][, using])
也是位於 django.template.loader 中,用於將模板渲染后返回字符串。
template_name:模板名稱,如果是一個字符串,將調用 get_template() 方法;如果是一個列表將調用 select_template() 方法。
context:一個python字典,用來渲染模板。
context_instance:從1.8版本起廢棄,這里不再討論。
request:必須是一個 HttpRequest 對象,並且在整個模版渲染期都是可用的。
using:用來指定查找模板的引擎。
快捷函數
一般而言我們可以將渲染后的字符串作為 HttpResponse 的第一個參數,作為構建響應報文的主體。
由於這個動作實在太常有了:加載--渲染--返回;所以django提供了兩個快捷函數來處理這些事務。
這兩個位於 django.shortcuts 模塊中,在使用前記得先導入。
render(request, template_name[, context][, context_instance][, content_type][, status][, current_app][, dirs][, using])
這個函數實現查找,加載,渲染,構建 HttpResponse 對象一整套流程,所以我們可以使用它來節省許多功夫。
request:用於生成 response 對象的 request 對象。
template_name:模板名稱,或者是包含許多模板名稱的列表。
context:渲染使用的python字典。
context_instance:1.8版本起廢棄,這里不再討論。
content_type:指定 MIME 類型,默認為 DEFAULT_CONTENT_TYPE 的值。
status:響應的狀態碼,默認為200.
current_app:指示哪個應用包含當前的視圖。1.8版本后廢棄,現在要設置 request.current_app 進行代替。
using:用於加載模板使用的模板引擎的名稱。
例子:
from django.shortcuts import render def my_view(request): # View code here... return render(request, 'myapp/index.html', {"foo": "bar"}, content_type="application/xhtml+xml")
這個示例等同於:
from django.http import HttpResponse from django.template import RequestContext, loader def my_view(request): # View code here... t = loader.get_template('myapp/index.html') c = RequestContext(request, {'foo': 'bar'}) return HttpResponse(t.render(c), content_type="application/xhtml+xml")
2. render_to_response(template_name[, context][, context_instance][, content_type][, status][, dirs][, using])
參數和上面的一樣,就是少了 request 參數。
例子:
from django.shortcuts import render_to_response def my_view(request): # View code here... return render_to_response('myapp/index.html', {"foo": "bar"}, content_type="application/xhtml+xml")
這個示例等同於:
from django.http import HttpResponse from django.template import Context, loader def my_view(request): # View code here... t = loader.get_template('myapp/index.html') c = Context({'foo': 'bar'}) return HttpResponse(t.render(c), content_type="application/xhtml+xml")
在這里,我們看到了兩個沒有見過的函數:Context();RequestContext()。並且渲染的時候使用的是這兩個函數的返回值,並不是標准的python字典。這和官方文檔的說法貌似有點矛盾,其原文是這樣的:If context
is provided, it must be a dict
. If it isn’t provided, the engine will render the template with an empty context.
而我初步看了這兩個類的代碼后,認為其應該是對於python字典的一種擴展,所以稱其為 dict 也還是說得過去的。不過當我們使用快捷函數的時候,所傳遞的依然是python的標准字典,因為快捷函數內部做了轉換的處理,關於這兩個函數,等我研究一番后在說。
另外,這里要補充一點,當開啟了 CSRF 中間件后,並處理 POST 的表單時,請使用 render 函數,而不是 render_to_response,只有 render 函數會處理 csrf token,否則你可能會面對 csrf 的報錯。