Tornado 包含一種簡(jiǎn)單、快速且靈活的模板語(yǔ)言。本節(jié)介紹該語(yǔ)言以及國(guó)際化等相關(guān)問(wèn)題。
Tornado 也可以與任何其他 Python 模板語(yǔ)言一起使用,盡管沒(méi)有將這些系統(tǒng)集成到 RequestHandler.render. 只需將模板呈現(xiàn)為字符串并將其傳遞給RequestHandler.write
默認(rèn)情況下,Tornado 在與引用它們的 ?.py
?文件相同的目錄中查找模板文件。 要將您的模板文件放在不同的目錄中,請(qǐng)使用 ?template_path
? 應(yīng)用程序設(shè)置(或者如果您對(duì)不同的處理程序有不同的模板路徑,則覆蓋 ?RequestHandler.get_template_path?)。
要從非文件系統(tǒng)位置加載模板,子類 ?tornado.template.BaseLoader?傳遞一個(gè)實(shí)例作為?template_loader
?應(yīng)用程序設(shè)置。
編譯后的模板默認(rèn)緩存; 要關(guān)閉此緩存并重新加載模板以對(duì)底層文件的更改始終可見(jiàn),請(qǐng)使用應(yīng)用程序設(shè)置?compiled_template_cache=False
? 或?debug=True
?。
Tornado 模板只是 HTML(或任何其他基于文本的格式),在標(biāo)記中嵌入了 Python 控制序列和表達(dá)式:
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<ul>
{% for item in items %}
<li>{{ escape(item) }}</li>
{% end %}
</ul>
</body>
</html>
如果您將此模板保存為“template.html”并將其放在與 Python 文件相同的目錄中,您可以使用以下方式渲染此模板:
class MainHandler(tornado.web.RequestHandler):
def get(self):
items = ["Item 1", "Item 2", "Item 3"]
self.render("template.html", title="My title", items=items)
Tornado 模板支持控制語(yǔ)句和表達(dá)式。 控制語(yǔ)句被 ?{%
? 和 ?%}
? 包圍,例如 ?{% if len(items) > 2 %}
?。 表達(dá)式被 ?{{
? 和 ?}}
? 包圍,例如 ?{{ item[0] }}
?。
控制語(yǔ)句大多數(shù)精確地映射到 Python 語(yǔ)句。 它支持 ?if
?、?for
?、?while
?和 ?try
?,它們都以 ?{% end %}
? 結(jié)束。 它還支持使用 ?extends
?和 ?block
?語(yǔ)句的模板繼承,在 tornado.template 的文檔中有詳細(xì)描述。
表達(dá)式可以是任何 Python 表達(dá)式,包括函數(shù)調(diào)用。 模板代碼在包含以下對(duì)象和函數(shù)的命名空間中執(zhí)行。 (請(qǐng)注意,此列表適用于使用 RequestHandler.render 和 render_string 呈現(xiàn)的模板。如果您直接在 RequestHandler 之外使用 tornado.template 模塊,則其中許多條目不存在)。
當(dāng)你構(gòu)建一個(gè)真正的應(yīng)用程序時(shí),你會(huì)想要使用 Tornado 模板的所有特性,尤其是模板繼承。閱讀本節(jié)中有關(guān)這些功能的所有信息tornado.template (一些功能,包括?UIModules
?在 tornado.web模塊中實(shí)現(xiàn))
在底層,Tornado 模板被直接翻譯成 Python。您包含在模板中的表達(dá)式被逐字復(fù)制到代表您的模板的 Python 函數(shù)中。它不會(huì)試圖阻止模板語(yǔ)言中的任何內(nèi)容;它明確創(chuàng)建它是為了提供其他更嚴(yán)格的模板系統(tǒng)所阻止的靈活性。因此,如果您在模板表達(dá)式中編寫(xiě)隨機(jī)內(nèi)容,則在執(zhí)行模板時(shí)會(huì)出現(xiàn)隨機(jī) Python 錯(cuò)誤。
默認(rèn)情況下,使用 tornado.escape.xhtml_escape 函數(shù)對(duì)所有模板輸出進(jìn)行轉(zhuǎn)義。 可以通過(guò)將 ?autoescape=None
? 傳遞給 Application 或 tornado.template.Loader 構(gòu)造函數(shù)來(lái)全局更改此行為,對(duì)于具有 ?{% autoescape None %}
? 指令的模板文件,或者通過(guò)替換 ?{{ ... }}
? 來(lái)更改單個(gè)表達(dá)式 使用 ?{% raw ...%}
?。 此外,在這里的每一個(gè)地方,都可以使用替代轉(zhuǎn)義函數(shù)的名稱來(lái)代替 ?None
?。
請(qǐng)注意,雖然 Tornado 的自動(dòng)轉(zhuǎn)義有助于避免 XSS 漏洞,但并非在所有情況下都足夠。 出現(xiàn)在某些位置的表達(dá)式,例如 JavaScript 或 CSS,可能需要額外的轉(zhuǎn)義。 此外,必須注意始終在可能包含不受信任的內(nèi)容的 HTML 屬性中使用雙引號(hào)和 xhtml_escape,或者必須為屬性使用單獨(dú)的轉(zhuǎn)義函數(shù)(參見(jiàn)例如此博客文章)。
當(dāng)前用戶的語(yǔ)言環(huán)境(無(wú)論他們是否登錄)始終在請(qǐng)求處理程序中作為?self.locale
?和在模板中作為?locale
?可用。 語(yǔ)言環(huán)境的名稱(例如 ?en_US
?)以 ?locale.name
?的形式提供,您可以使用 Locale.translate 方法翻譯字符串。 模板還具有可用于字符串翻譯的全局函數(shù)調(diào)用 ?_()
?。 translate函數(shù)有兩種形式:
_("Translate this string")
它直接根據(jù)當(dāng)前語(yǔ)言環(huán)境翻譯字符串,并且:
_("A person liked this", "%(num)d people liked this",
len(people)) % {"num": len(people)}
它根據(jù)第三個(gè)參數(shù)的值翻譯一個(gè)可以是單數(shù)或復(fù)數(shù)的字符串。 在上面的示例中,如果 ?len(people)
? 為 ?1
?,則返回第一個(gè)字符串的翻譯,否則返回第二個(gè)字符串的翻譯。
最常見(jiàn)的翻譯模式是對(duì)變量使用 Python 命名的占位符(上例中的 %(num)d),因?yàn)檎嘉环梢栽诜g時(shí)移動(dòng)。
以下是一個(gè)正確國(guó)際化的模板:
<html>
<head>
<title>FriendFeed - {{ _("Sign in") }}</title>
</head>
<body>
<form action="{{ request.path }}" method="post">
<div>{{ _("Username") }} <input type="text" name="username"/></div>
<div>{{ _("Password") }} <input type="password" name="password"/></div>
<div><input type="submit" value="{{ _("Sign in") }}"/></div>
{% module xsrf_form_html() %}
</form>
</body>
</html>
默認(rèn)情況下,我們使用用戶瀏覽器發(fā)送的 ?Accept-Language
? 表頭檢測(cè)用戶的語(yǔ)言環(huán)境。 如果找不到合適的 ?Accept-Language
? 值,我們選擇 ?en_US
?。 如果您讓用戶將其區(qū)域設(shè)置為首選項(xiàng),則可以通過(guò)覆蓋 RequestHandler.get_user_locale 來(lái)覆蓋此默認(rèn)區(qū)域選擇:
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
user_id = self.get_secure_cookie("user")
if not user_id: return None
return self.backend.get_user_by_id(user_id)
def get_user_locale(self):
if "locale" not in self.current_user.prefs:
# Use the Accept-Language header
return None
return self.current_user.prefs["locale"]
如果 get_user_locale 返回 None,我們將使用 Accept-Language 表頭。
tornado.locale 模塊支持以兩種格式加載翻譯:gettext 和相關(guān)工具使用的 ?.mo
? 格式,以及簡(jiǎn)單的 ?.csv
? 格式。 應(yīng)用程序通常會(huì)在啟動(dòng)時(shí)調(diào)用一次 tornado.locale.load_translations 或 tornado.locale.load_gettext_translations
您可以使用 tornado.locale.get_supported_locales() 獲取應(yīng)用程序中支持的語(yǔ)言環(huán)境列表。 根據(jù)支持的語(yǔ)言環(huán)境,用戶的語(yǔ)言環(huán)境被選擇為最接近的匹配。 例如,如果用戶的語(yǔ)言環(huán)境是 ?es_GT
?,并且支持 ?es
? 語(yǔ)言環(huán)境,那么對(duì)于該請(qǐng)求,?self.locale
? 將是 ?es
?。 如果找不到匹配項(xiàng),我們將使用 ?en_US
?。
Tornado 支持 UI 模塊,以便在您的應(yīng)用程序中輕松支持標(biāo)準(zhǔn)的、可重用的 UI 小部件。 UI 模塊就像渲染頁(yè)面組件的特殊函數(shù)調(diào)用,它們可以使用自己的 CSS 和 JavaScript 打包。
例如,如果你正在實(shí)現(xiàn)一個(gè)博客,并且你想讓博客條目出現(xiàn)在博客主頁(yè)和每個(gè)博客條目頁(yè)面上,你可以制作一個(gè)?Entry
?模塊來(lái)在兩個(gè)頁(yè)面上呈現(xiàn)它們。 首先,為您的 UI 模塊創(chuàng)建一個(gè) Python 模塊,例如 ?uimodules.py
?:
class Entry(tornado.web.UIModule):
def render(self, entry, show_comments=False):
return self.render_string(
"module-entry.html", entry=entry, show_comments=show_comments)
使用應(yīng)用程序中的 ?ui_modules
? 設(shè)置告訴 Tornado 使用 ?uimodules.py
?:
from . import uimodules
class HomeHandler(tornado.web.RequestHandler):
def get(self):
entries = self.db.query("SELECT * FROM entries ORDER BY date DESC")
self.render("home.html", entries=entries)
class EntryHandler(tornado.web.RequestHandler):
def get(self, entry_id):
entry = self.db.get("SELECT * FROM entries WHERE id = %s", entry_id)
if not entry: raise tornado.web.HTTPError(404)
self.render("entry.html", entry=entry)
settings = {
"ui_modules": uimodules,
}
application = tornado.web.Application([
(r"/", HomeHandler),
(r"/entry/([0-9]+)", EntryHandler),
], **settings)
在模板中,您可以使用 ?{% module %}
? 語(yǔ)句調(diào)用模塊。 例如,您可以從 ?home.html
? 中調(diào)用 ?Entry
? 模塊:
{% for entry in entries %}
{% module Entry(entry) %}
{% end %}
?entry.html
?:
{% module Entry(entry, show_comments=True) %}
模塊可以通過(guò)覆蓋 ?embedded_css
?、?embedded_javascript
?、?javascript_files
? 或 ?css_files
? 方法來(lái)包含自定義 CSS 和 JavaScript 函數(shù):
class Entry(tornado.web.UIModule):
def embedded_css(self):
return ".entry { margin-bottom: 1em; }"
def render(self, entry, show_comments=False):
return self.render_string(
"module-entry.html", show_comments=show_comments)
無(wú)論模塊在頁(yè)面上使用多少次,模塊 CSS 和 JavaScript 都會(huì)被包含一次。 CSS 總是包含在頁(yè)面的 ?<head>
? 中,而 JavaScript 總是包含在頁(yè)面末尾的 ?</body>
? 標(biāo)記之前。
當(dāng)不需要額外的 Python 代碼時(shí),可以將模板文件本身用作模塊。 例如,可以重寫(xiě)前面的示例以將以下內(nèi)容放入 ?module-entry.html
? 中:
{{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }}
<!-- more template html... -->
這個(gè)修改后的模板模塊將被調(diào)用:
{% module Template("module-entry.html", show_comments=True) %}
?set_resources
? 函數(shù)僅在通過(guò) ?{% module Template(...) %}
? 調(diào)用的模板中可用。 與 ?{% include ... %}
? 指令不同,模板模塊具有與其包含模板不同的命名空間——它們只能看到全局模板命名空間和它們自己的關(guān)鍵字參數(shù)。
更多建議: