Django 中模板變量使用
上一節(jié)中,我們簡(jiǎn)單介紹了模板相關(guān)的概念,并手動(dòng)實(shí)踐了 python 中的兩個(gè)流行的模板庫(kù) Mako 和 Jinja2。接下來,我們將深入了解如何在 Django 中使用模板系統(tǒng)。
1. Django 中的模板系統(tǒng)
Django 中自帶了一個(gè)模板系統(tǒng),叫做 Django Template Language (DTL),通過該引擎,我們可以方便的加載模板文件到內(nèi)存中進(jìn)行編譯并動(dòng)態(tài)插入數(shù)據(jù),最后返回轉(zhuǎn)換后的文本內(nèi)容。在 Django 中模板引擎的配置同樣是在 settings.py 中的,具體配置如下:
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',
],
},
},
]
如果我們想讓 Django 使用 Jinja2 模板引擎,可以將上述配置中的 BACKEND
的值進(jìn)行替換:
'BACKEND': 'django.template.backends.jinja2.Jinja2',
又或者我們可以同時(shí)支持多個(gè)模板引擎,只不過指定的模板目錄不同即可:
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',
],
},
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['/root/test/html/jinja2'],
}
]
Django 中提供了兩個(gè)方法加載模板:get_template() 方法和 select_template() 方法。從源碼中可以看到,這兩個(gè)方法的實(shí)現(xiàn)幾乎是一樣的,只不過前者只能輸入一個(gè)模板文件并返回 Template 對(duì)象,而后者可以輸入多個(gè)模板文件但也只返回第一個(gè)存在的模板文件的 Template 對(duì)象。
# django/template/loader.py
from . import engines
from .exceptions import TemplateDoesNotExist
def get_template(template_name, using=None):
"""
Load and return a template for the given name.
Raise TemplateDoesNotExist if no such template exists.
"""
chain = []
engines = _engine_list(using)
# 遍歷模板引擎
for engine in engines:
try:
return engine.get_template(template_name)
except TemplateDoesNotExist as e:
chain.append(e)
# 找不到匹配的模板引擎,就直接拋出TemplateDoesNotExist異常
raise TemplateDoesNotExist(template_name, chain=chain)
def select_template(template_name_list, using=None):
"""
Load and return a template for one of the given names.
Try names in order and return the first template found.
Raise TemplateDoesNotExist if no such template exists.
"""
if isinstance(template_name_list, str):
raise TypeError(
'select_template() takes an iterable of template names but got a '
'string: %r. Use get_template() if you want to load a single '
'template by name.' % template_name_list
)
chain = []
engines = _engine_list(using)
for template_name in template_name_list:
for engine in engines:
try:
return engine.get_template(template_name)
except TemplateDoesNotExist as e:
chain.append(e)
if template_name_list:
raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain)
else:
raise TemplateDoesNotExist("No template names provided")
# 忽略其他代碼
從 django 的源代碼中,可以看到 _engine_list() 方法就是獲取我們前面的引擎列表,然后開始找這個(gè)引擎對(duì)應(yīng)的目錄下是否有輸入的模板文件。如果一旦根據(jù)模板文件找到了匹配的模板引擎,查詢工作就會(huì)停止,直接使用該模板引擎對(duì)模板文件生成一個(gè) Template 對(duì)象并返回。
對(duì)于不同的模板引擎,返回的 Template 對(duì)象是不同的,但是 Template 對(duì)象必須包含一個(gè) render() 方法:
Template.render(context=None, request=None)
接下來,我們會(huì)使用兩個(gè)模板文件進(jìn)行一個(gè)簡(jiǎn)單的實(shí)驗(yàn)。實(shí)驗(yàn)的環(huán)境還是在原來的 first-django-app 工程中進(jìn)行。首先,我們準(zhǔn)備兩個(gè)模板文件 index.html 和 index.html.j2。其中 index.html 放到 django 工程下的 template 目錄中,對(duì)應(yīng)著 Django 內(nèi)置的模板引擎; index.html.j2 則放到 /root/test/html/ 目錄下,對(duì)應(yīng)著 jinja2 模板引擎。
(django-manual) [root@server first_django_app]# python manage.py shell
Python 3.8.1 (default, Dec 24 2019, 17:04:00)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.template.loader import get_template
>>> get_template('index.html')
<django.template.backends.django.Template object at 0x7f132afe08e0>
>>> get_template('index.html.j2')
<django.template.backends.jinja2.Template object at 0x7f132a952a60>
>>>
可以看到,對(duì)應(yīng)的模板文件都找到了對(duì)應(yīng)的模板引擎。接下來,我們來對(duì) Django 內(nèi)置的模板引擎進(jìn)行測(cè)試:
(django-manual) [root@server first_django_app]# cat templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</body>
</html>
(django-manual) [root@server first_django_app]# python manage.py shell
Python 3.8.1 (default, Dec 24 2019, 17:04:00)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.template.loader import get_template
>>> tf = get_template('index.html')
>>> print(tf.render(context={'title': '標(biāo)題文件', 'content': '這是正文'}))
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>標(biāo)題文件</h1>
<p>這是正文</p>
</body>
</html>
可以看到,Django 內(nèi)置的模板引擎已經(jīng)正確地將模板文件進(jìn)行了轉(zhuǎn)換,將模板變量進(jìn)行替換,得到了我們想要的HTML 文本。
2. Django 中的 render 函數(shù)
有了上面的基礎(chǔ),我們來看看前面給出的返回動(dòng)態(tài) HTML 的 render 函數(shù):
def index(request, *args, **kwargs):
return render(request, "index.html", {"title":"首頁(yè)", "content": "這是正文"})
我們可以通過源碼追蹤以下該函數(shù)的執(zhí)行過程。首先 render 函數(shù)定義在 django/shortcut.py 中,代碼內(nèi)容如下:
# django/shortcut.py
# ...
def render(request, template_name, context=None, content_type=None, status=None, using=None):
"""
Return a HttpResponse whose content is filled with the result of calling
django.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)
# ...
可以看到,最核心的轉(zhuǎn)換代碼是 loader.render_to_string
方法,我們繼續(xù)追進(jìn)去,就可以看到和前面熟悉的測(cè)試代碼了:
# django/template/loader.py
# ...
def render_to_string(template_name, context=None, request=None, using=None):
"""
Load a template and render it with a context. Return a string.
template_name may be a string or a list of strings.
"""
if isinstance(template_name, (list, tuple)):
template = select_template(template_name, using=using)
else:
template = get_template(template_name, using=using)
return template.render(context, request)
# ...
render_to_string()
方法的第一步是判斷 template_name
是列表還是元組。如果是列表或者元祖,則使用select_template()
方法得到對(duì)應(yīng)的模板對(duì)象,否則使用 get_template()
方法。最后由template.render()
方法加上對(duì)應(yīng)的模板數(shù)據(jù)生成我們想要的 HTML 文本??梢钥吹阶詈蟮?render()
方法依舊是返回的 HttpResponse
示例,只不過 content 參數(shù)正好是動(dòng)態(tài)生成的 HTML 文本。
3. 小結(jié)
今天我們探究了 Django 中模板系統(tǒng)的配置和使用。此外還追蹤了 render() 方法的源碼,對(duì) Django 中的模板系統(tǒng)以及提供的一些封裝的內(nèi)部函數(shù)有了更清楚的認(rèn)識(shí)。接下來,我們將深入學(xué)習(xí) Django 內(nèi)置的模板語(yǔ)言-DTL,徹底掌握復(fù)雜模板文件的編寫。