Django 2.1.3 文件-模板-概述
模板
作為一個Web框架,Django需要一種動態生成HTML的便捷方法。最常用的方法依賴於模板。模板包含所需HTML輸出的靜態部分以及描述動態內容將被插入的一些特殊語法。有關建立帶有模板的HTML頁面的示例,請參閱:示例3
Django專案可以配置一個或多個模板引擎(或者不使用模板)。Django後端內建一個自己的模板系統,創造性地稱為Django template language(DTL)和一個流行的替代品
Django定義了一個標準的API,用於載入和渲染模板,而不用考慮後端的模板系統。載入包括查詢給定識別符號的模板並對其進行預處理,通常將其編譯的結果儲存在記憶體中。渲染工具將上下文資料插入模板並返回結果字串。
Django 模板語言 是Django自己的模板系統。直到Django 1.8,它是唯一可用的內建選項。這是一個很好的模板庫,即使它是相當僵硬和使用時帶有它自己特質。如果您沒有緊迫的理由需要去選擇另一個後端,則應該使用DTL,特別是如果您正在編寫可插入的應用程式並打算分發模板。在 Django’s contrib apps 中的有些模板,比如
由於歷史原因,模板引擎的通用支援和Django模板語言的實現都存在於django.template
模組的名稱空間中。
警告
模板系統使用不可信的模板作者的模板是不安全的。例如,一個站點不應該允許它的使用者提供他們自己的模板,因為模板作者可以做一些事情,比如執行XSS攻擊和拿到包含敏感資訊的模板變數的訪問權。
1. 模板引擎的支援
1.1 配置
模板引擎在TEMPLATES設定中配置。這是一個配置列表,每個引擎一個。預設值為空。在由startproject命令所產生的 settings.py檔案中定義一個有用的值:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
# ... some options here ...
},
},
]
BACKEND是實現Django模板後端API的模板引擎類的虛擬Python路徑。內建後端是django.template.backends.django.DjangoTemplates
和 django.template.backends.jinja2.Jinja2
。
由於大多數引擎從檔案載入模板,因此每個引擎的頂級配置包含兩個常用設定:
DIRS
定義了模板原始檔的目錄列表,引擎按搜尋順序查詢。APP_DIRS
告訴引擎是否應該在已安裝的應用程式中查詢模板。每個後端都定義應用程式中應儲存其模板的子目錄的常規名稱。
雖然不常見,但可以使用不同的選項配置同一後端的多個例項。在這種情況下,您應該為每個引擎定義唯一 的NAME。
OPTIONS 包含特定於後端的設定。
1.2 用法
django.template.loader模組定義了兩個用於載入模板的函式。
(1)get_template(template_name,using = None)
原始碼
此函式使用給定名稱載入模板並返回一個 Template物件。
返回值的確切型別取決於載入模板的後端。每個後端都有自己的Template類。
get_template()按順序嘗試每個模板引擎,直到成功。如果找不到模板,則會引發模板TemplateDoesNotExist
。如果找到模板但包含無效語法,則會引發該模板 TemplateSyntaxError
。
如何搜尋和載入模板取決於每個引擎的後端和配置。
如果要將搜尋限制為特定模板引擎,請在using引數中傳遞引擎NAME。
(2)select_template(template_name_list,using = None)
source
select_template()就像是get_template(),除了它採用模板名稱列表。它按順序嘗試每個名稱並返回存在的第一個模板。
如果載入模板失敗,則django.template可能會引發以下兩個異常:
(1)TemplateDoesNotExist(msg,tries = None,backend = None,chain = None)
source
無法找到模板時引發此異常。它接受以下可選引數,用於在除錯頁面上填充 模板postmortem:
- backend
發生異常的模板後端例項。 - tried
查詢模板時嘗試的源列表。這被格式化為包含(origin, status)元組的列表,其中origin是origin-like物件,並且status是一個字串,其中包含未找到模板的原因。 - chain
嘗試載入模板時引發的TemplateDoesNotExist
異常列表。這由get_template()嘗試從多個引擎載入給定模板的函式使用。
(2)TemplateSyntaxError(msg)
source
找到模板但包含錯誤時會引發此異常。
(3)Template.render(context = None,request = None)
使用給定的上下文呈現此模板。
通過get_template()和select_template() 返回的Template物件必須提供render()方法。
如果提供context,它必須是字典型別。如果未提供,則引擎將使用空上下文呈現模板。
如果提供request,它必須是HttpRequest。然後引擎必須在模板中提供它以及CSRF令牌。如何實現這一目標取決於每個後端。
這是搜尋演算法的一個例子。對於此示例, TEMPLATES設定為:
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將按如下順序查詢的檔案:
- /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(['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)
當Django找到存在的模板時,它會停止查詢。
小貼士
您可以使用select_template()提供靈活的模板載入。例如,如果您撰寫了新聞報道並希望某些故事具有自定義模板,請使用類似select_template(['story_%s_detail.html' % story.id, 'story_detail.html'])
的內容 。這將允許您為單個故事使用自定義模板,併為沒有自定義模板的故事提供後備模板。↑
在包含模板的每個目錄中的子目錄中組織模板是可能的 - 也是可取的。慣例是為每個Django應用程式建立一個子目錄,並根據需要在這些子目錄中包含子目錄。
這樣做是為了你自己的理智。將所有模板儲存在單個目錄的根級別會變得混亂。
要載入子目錄中的模板,只需使用斜槓,如下所示:
get_template('news/story_detail.html')
使用與上面TEMPLATES相同的選項,這將嘗試載入以下模板:
- /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)
此外,為了減少載入和渲染模板的重複性,Django提供了一個自動化過程的快捷功能。
render_to_string(template_name,context = None,request = None,using = None)
source
render_to_string() 載入一個模板,如 get_template() ,並立即呼叫它的 render() 方法。它需要下面的引數。
- template_name
載入和呈現模板的名稱。如果是模板名稱列表,Django 使用 select_template() ,而不是 get_template() 找到模板。 - context
一個字典用作模板的渲染上下文。 - request
可選項 HttpRequest 在模板的渲染過程中可用。 - using
可選的模板引擎 NAME。對模板的搜尋將限於該引擎。
使用例項:
from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', {'foo': 'bar'})
還可以參看 render() 快捷函式,它呼叫 render_to_string() ,並將結果提供給 HttpResponse ,適合從檢視返回。
最後,您可以直接使用配置好的引擎:
engines
模板引擎可在 django.template.engines
中使用:
from django.template import engines
django_engine = engines['django']
template = django_engine.from_string("Hello {{ name }}!")
在這個例子中,查詢的鍵“django”是引擎的 NAME。
1.3 內建後端
1.3.1 DjangoTemplates(預設後端)
class DjangoTemplates
原始碼
設定BACKEND
為'django.template.backends.django.DjangoTemplates'
,以配置Django模板引擎。
如果APP_DIRS
是True,DjangoTemplates 引擎在應用程式的templates的子目錄內尋找模板檔案。保留此通用名稱是為了向後相容。
DjangoTemplates引擎接受以下OPTIONS
:
-
'autoescape'
:一個控制是否啟用HTML自動轉義的布林值。
它預設為True。
警告 --> 只有在渲染非HTML模板時才設定為False! -
'context_processors'
:用於在使用請求呈現模板時用於填充上下文的可調整的python路徑列表。這些可呼叫物件將請求物件作為其引數,並返回 一個字典而且整合到上下文中。
預設為空列表。有關RequestContext更多資訊。 -
'debug'
:一個開啟/關閉模板除錯模式的布林值。如果是 True,則錯誤頁面將顯示模板呈現期間引發的任何異常的詳細報告。此報告包含模板的相關片段,並突出顯示相應的行。
它預設為settings.py
檔案中DEBUG
設定的值。 -
'loaders'
:模板載入器類的虛擬Python路徑列表。每個Loader類都知道如何從特定源匯入模板。可選地,可以使用元組而不是字串。元組中的第一項應該是Loader類名,後續項將Loader在初始化期間傳遞給它。
*預設值取決於值DIRS
和APP_DIRS
。*有關詳細資訊,請參閱加載器類 -
'string_if_invalid'
:作為字串輸出,模板系統應該用於無效(例如拼寫錯誤的)變數。
*預設為空字串。*有關詳細資訊,請參見如何處理無效變數 -
'file_charset'
:用於讀取磁碟上的模板檔案的字符集。
預設值為FILE_CHARSET
。 -
'libraries'
:用於向模板引擎註冊的模板標記模組的標籤和點綴Python路徑的字典。這可用於新增新庫或為現有庫提供備用標籤。例如:
OPTIONS={
'libraries': {
'myapp_tags': 'path.to.myapp.tags',
'admin.urls': 'django.contrib.admin.templatetags.admin_urls',
},
}
可以通過將相應的字典的鍵傳遞給{% load %}
標籤來載入庫。
{% load static %}
'builtins'
:要新增到內建函式的模板標記模組的虛擬Python路徑列表。例如:
OPTIONS={
'builtins': ['myapp.builtins'],
}
無需先呼叫{% load %}
標籤即可使用內建庫中的標記和過濾器。
1.3.2 Jinja2
pip install Jinja2
設定BACKEND為 'django.template.backends.jinja2.Jinja2'
配置Jinja2引擎。
如果APP_DIRS
是True,Jinja2引擎尋找在安裝應用程式的jinja2的子目錄內的模板。
最重要的OPTIONS條目是'environment'
。它是返回Jinja2環境的可呼叫的Python路徑。它預設為'jinja2.Environment'
。Django呼叫它並將其他選項作為關鍵字引數傳遞。此外,Django添加了與Jinja2不同的預設選項:
'autoescape'
: True'loader'
:為DIRS和 APP_DIRS配置的載入器'auto_reload'
: settings.DEBUG'undefined'
:DebugUndefined if settings.DEBUG else Undefined
Jinja2引擎也接受以下OPTIONS:
'context_processors'
:用於在使用請求呈現模板時用於填充上下文的可調整的python路徑列表。這些可呼叫物件將請求物件作為其引數,並返回 一個字典而且整合到上下文中。
預設為空列表。
不鼓勵在Jinja2中使用context_processors。
上下文處理器對Django模板很有用,因為Django模板不支援使用引數呼叫函式。由於Jinja2沒有這個限制,因此建議將使用上下文處理器的函式放在模板可用的全域性變數 jinja2.Environment
中,如下所述。然後,您可以在模板中呼叫該函式:
{{ function(request) }}
一些Django模板上下文處理器返回固定值。對於Jinja2模板,這個間接層不是必需的,因為您可以直接新增常量jinja2.Environment
。
為Jinja2新增context_processors的原始用法包括:
- 根據請求進行耗時的計算。
- 需要每個模板的結果。
- 在每個模板中多次使用結果。
除非滿足所有這些條件,否則將函式傳遞給模板更簡單,更符合Jinja2的設計。
有目的地將預設配置保持在最低限度。如果一個模板與一個請求呈現(例如,使用時render())時,Jinja2後端在上下問中新增全域性request,csrf_input和 csrf_token。除此之外,這個後端不會建立一個Django風格的環境。它不知道Django過濾器和標籤。要使用特定於Django的API,必須將它們配置到環境中。
例如,您可以建立myproject/jinja2.py
,使用以下內容:
from django.templatetags.static import static
from django.urls import reverse
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': static,
'url': reverse,
})
return env
並將'environment'
選項設定為'myproject.jinja2.environment'
。
然後你可以在Jinja2模板中使用以下結構:
<img src="{{ static('path/to/company-logo.png') }}" alt="Company Logo">
<a href="{{ url('admin:index') }}">Administration</a>
標籤和過濾器的概念既存在於Django模板語言中,也存在於Jinja2中,但它們的使用方式不同。由於Jinja2支援將引數傳遞給模板中的可呼叫物件,因此可以通過呼叫Jinja2模板中的函式來實現許多需要Django模板中的模板標記或過濾器的功能,如上例所示。Jinja2的全域性名稱空間消除了對模板上下文處理器的需求。Django模板語言沒有等價於Jinja2的測試。
1.3.3 自定義後端
以下是如何實現自定義模板後端以便使用其他模板系統。一個模板後端是一個繼承自django.template.backends.base.BaseEngine
的類。它必須實現 get_template()和可選的from_string()。這是一個虛構的foobar模板庫的示例:
from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.template.backends.base import BaseEngine
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy
import foobar
class FooBar(BaseEngine):
# Name of the subdirectory containing the templates for this engine
# inside an installed application.
app_dirname = 'foobar'
def __init__(self, params):
params = params.copy()
options = params.pop('OPTIONS').copy()
super().__init__(params)
self.engine = foobar.Engine(**options)
def from_string(self, template_code):
try:
return Template(self.engine.from_string(template_code))
except foobar.TemplateCompilationFailed as exc:
raise TemplateSyntaxError(exc.args)
def get_template(self, template_name):
try:
return Template(self.engine.get_template(template_name))
except foobar.TemplateNotFound as exc:
raise TemplateDoesNotExist(exc.args, backend=self)
except foobar.TemplateCompilationFailed as exc:
raise TemplateSyntaxError(exc.args)
class Template:
def __init__(self, template):
self.template = template
def render(self, context=None, request=None):
if context is None:
context = {}
if request is not None:
context['request'] = request
context['csrf_input'] = csrf_input_lazy(request)
context['csrf_token'] = csrf_token_lazy(request)
return self.template.render(context)
有關更多資訊,請參見DEP 182。
1.4 自定義引擎的除錯整合
Django除錯頁面有鉤子,可在出現模板錯誤時提供詳細資訊。自定義模板引擎可以使用這些掛鉤來增強向用戶顯示的回溯資訊。以下鉤子可用:
1.4.1 Template postmortem
postmortem會同TemplateDoesNotExist一起出現。它列出了在嘗試查詢給定模板時使用的模板引擎和載入器。例如,如果配置了兩個Django引擎,則postmortem將顯示為:
自定義引擎可以通過在引發TemplateDoesNotExist
時傳遞backend
和tried
引數來填充時間。使用了postmortem的後端應該在模板物件上specify an origin。
1.4.2 行的上下文資訊
如果在模板解析或渲染過程中發生錯誤,Django可以顯示錯誤發生的行。例如:
自定義引擎可以填充這些資訊,通過在解析和渲染期間引發的異常上設定template_debug
屬性。此屬性字典具有以下值:
鍵的屬性 | 含義 |
---|---|
‘name’ | 發生異常的模板的名稱。 |
‘message’ | 異常訊息 |
‘source_lines’ | 行之前,之後和包括髮生異常的行。這是針對上下文的,所以它不應該包含超過20行左右 |
‘line’ | 發生異常的行。 |
‘before’ | 引發錯誤的token之前的錯誤行上的內容 |
‘during’ | 引發錯誤的token |
‘after’ | 引發錯誤的token後錯誤行上的內容 |
‘total’ | source_lines中的行數 |
‘top’ | source_lines中開始的行號 |
‘bottom’ | source_lines中結束的行號 |
鑑於上面的模板錯誤,template_debug看起來像這樣:
{
'name': '/path/to/template.html',
'message': "Invalid block tag: 'syntax'",
'source_lines': [
(1, 'some\n'),
(2, 'lines\n'),
(3, 'before\n'),
(4, 'Hello {% syntax error %} {{ world }}\n'),
(5, 'some\n'),
(6, 'lines\n'),
(7, 'after\n'),
(8, ''),
],
'line': 4,
'before': 'Hello ',
'during': '{% syntax error %}',
'after': ' {{ world }}\n',
'total': 9,
'bottom': 9,
'top': 1,
}
1.4.3 Origin API和第三方整合
Django模板提供了一個Origin物件,通過template.origin
屬性。這使得除錯資訊可以顯示在模板postmortem中,也可以顯示在第三方庫中,如Django Debug Toolbar。
自定義引擎可以提供自己的template.origin
資訊通過建立指定以下屬性的物件:
'name'
:模板的完整路徑。'template_name'
:傳遞到模板載入方法的模板的相對路徑。'loader_name'
:標識用於載入模板的函式或類的可選字串,例如django.template.loaders.filesystem.Loader
。
2.Django 模板語言
2.1 語法
關於本節
這是Django模板語言語法的概述。有關詳情請參閱 語言的語法參考。
Django模板只是一個文字文件或使用Django模板語言標記的Python字串。一些構造由模板引擎識別和解釋。主要是變數和標籤。
使用上下文呈現模板。渲染將變數替換為其值,這些值在上下文中查詢,並執行標記。其他所有內容都按原樣輸出。
Django模板語言的語法涉及一下四種結構。
2.1.1 變數
變數從上下文輸出一個值,這是一個類似於字典的物件,它將鍵對映到值。
變數被{{}}
包圍:
My first name is {{ first_name }}. My last name is {{ last_name }}.
在上下文中如果存在一個這樣的字典{'first_name': 'John', 'last_name': 'Doe'}
,此模板呈現為:
My first name is John. My last name is Doe.
字典查詢,屬性查詢和列表索引查詢都可以使用點表示法實現:
{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}
如果變數解析為可呼叫的函式,則模板系統將呼叫它而不帶引數,並使用其結果進行渲染。
譯者例項:
(1)在views.py
中定義如下:
class Classval:
def __init__(self):
self.val = 10
self.dicts ={"id":id(self),"val":self.val}
self.lists = ["Classval",self.val]
def getpow(self):
return pow(self.val,3)
def index(request):
cv = Classval()
return render(request,'model_layer/index.html',locals())
(2)在model_layer/index.html
中定義如下html內容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
cv.val:{{ cv.val }}<hr>
cv.dicts.id:{{ cv.dicts.id }}<hr>
cv.dicts.val:{{ cv.dicts.val }}<hr>
cv.lists.0:{{ cv.lists.0 }}<hr>
cv.lists.1:{{ cv.lists.1 }}<hr>
cv.getpow:{{ cv.getpow }}<hr>
</body>
</html>
(3)輸出結果
2.1.2 標籤
標籤在渲染過程中提供任意邏輯。
這個定義是故意模糊的。例如,標籤可以輸出內容,用作控制結構,例如“if”語句或“for”迴圈,從資料庫中獲取內容,或者甚至允許訪問其他模板標籤。
標籤被{%%}
包圍,比如:
{% csrf_token %}
大多數標籤接受引數:
{% cycle 'odd' 'even' %}
有些標籤需要開始和結束標籤:
{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}
譯者提供了 內建標籤 的參考以及編寫自定義標籤的說明。
2.1.1 過濾器
過濾器轉換變數和標籤引數的值。
它們看起來像這樣:
{{ django|title }}
在上下文中如果有此變數{'django': 'the web framework for perfectionists with deadlines'}
,此模板呈現為:
The Web Framework For Perfectionists With Deadlines
一些過濾器還接收引數:
{{ my_date|date:"Y-m-d" }}
譯者提供了內建過濾器 的參考以及 編寫自定義過濾器 的說明。
2.1.1 註釋
兩種註釋:單行註釋({# ... #}
)和多行註釋{% comment %}...{% endcomment %}
。
{# hello 單行註釋 #}
{% comment %}
多行註釋1
多行註釋2
...
{% endcomment %}
2.2 元件
關於本節
這是Django模板語言API的概述。有關詳細資訊,請參閱API參考。
2.2.1 引擎
django.template.Engine 封裝了Django模板系統的一個例項。Engine直接例項化的主要原因 是在Django專案之外使用Django模板語言。
django.template.backends.django.DjangoTemplates是一個適合Django模板後端API 的小型django.template.Engine包裝器。
2.2.1 模板
django.template.Template表示已編譯的模板。模板是用Engine.get_template()或Engine.from_string()來獲得的。
同樣,django.template.backends.django.Template是一個適合通用模板API 的小型django.template.Template包裝器。
2.2.1 上下文
django.template.Context 除了上下文資料之外還包含一些元資料。它被傳遞給Template.render()渲染模板。
django.template.RequestContext是Context儲存當前 HttpRequest和執行模板上下文處理器的子類 。
通用API沒有相同的概念。上下文資料以普通方式傳遞,如果需要,可以分開傳遞字典和當前HttpRequest。
2.2.1 載入器
模板載入器負責定位模板,載入模板和返回 Template 物件。
Django提供了 幾個內建模板載入器 並支援自定義模板載入器。
2.2.1 上下文處理器
上下文處理器是一個函式,接收當前 HttpRequest為引數並返回一個字典,字典裡是要新增到呈現上下文的資料。
它們的主要用途是將所有模板共享的公共資料新增到上下文中,而不必在每個檢視中重複程式碼。
Django提供了許多內建的上下文處理器。實現自定義上下文處理器就像定義函式一樣簡單。
完