导航
用户指南
模板与UI
Tornado包含一种简单,快速,灵活的模板语言。 本节介绍该语言以及国际化等相关问题。
Tornado可以任意使用其他的Python模板语言,哪怕它们并没有被集成到RequestHandler.render
中。 只需将模板渲染为字符串并将其传递给RequestHandler.write
即可。
配置模板
默认情况下,Tornado在与引用它们的.py文件的目录中查找模板文件。要将模板文件放在不同的目录中,请使用template_path
应用程序设置(如果不同的处理程序具有不同的模板路径,则修改RequestHandler.get_template_path
)即可。
要从非文件系统位置加载模板,请子类化tornado.template.BaseLoader
,并传递一个使用template_loader
应用程序设置的实例。
默认情况下,编译的模板会被缓存;要关闭此缓存和重新加载模板,以便始终可以看到对底层文件的更改,请使用应用程序设置compiled_template_cache = False
或debug = True
。
模板语法
Tornado模板只是使用Python控制语句和内嵌表达式标记的HTML(或任何其他基于文本的格式)。
<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模板支持控制语句和表达式。 控制语句由{%
和%}
包围,例如{% if len(items) > 2 %}
。 表达式由{{
和}}
包围,例如{{items [0]}}
。
控制语句或多或少地映射到Python语句。我们支持if
,for
,while
和try
,所有这些都以{% end% }
结束。我们还使用extends
和block
语句支持模板继承(template inheritance),这些语句在tornado.template
的文档中有详细描述。
表达式可以是包括函数调用的任何Python表达式。模板代码在包含以下对象和函数的命名空间中执行(请注意,此列表仅适用于使用RequestHandler.render
和render_string
渲染的模板。如果你直接在RequestHandler
之外使用tornado.template
模块,下面的许多项都将无法使用)。
escape
: tornado.escape.xhtml_escapexhtml_escape
: tornado.escape.xhtml_escapeurl_escape
: tornado.escape.url_escapejson_encode
: tornado.escape.json_encodesqueeze
: tornado.escape.squeezelinkify
: tornado.escape.linkifydatetime
: the Python datetime modulehandler
: the current RequestHandler objectrequest
: handler.requestcurrent_user
: handler.current_userlocale
: handler.locale_
: handler.locale.translatestatic_url
: handler.static_urlxsrf_form_html
: handler.xsrf_form_htmlreverse_url
: Application.reverse_url- 所有来自
ui_methods
和ui_modules``````Application
的设置项 - 所有传递给
render
或render_string
的关键词
在构建实际应用程序时,你将可能会使用到Tornado模板的所有功能,尤其是模板继承。 阅读tornado.template
部分中有关这些功能的所有内容(包括UIDodules
在内的某些在tornado.web
模块中实现的功能)
实际上Tornado模板在后台将直接转换为Python语言。你在模板中包含的表达式将逐字复制到表示模板的Python函数中。 我们不会试图阻止模板语言中的任何内容;我们创建模板时就为了提供比其他相对严格的模板系统中所缺少的灵活性。因此,如果在模板表达式中编写不受控制的内容,则在Python中执行模板时将会出现不可预知的错误。
默认情况下,使用tornado.escape.xhtml_escape
函数对所有模板输出进行转义。 可以通过将autoescape = None
传递给Application
或tornado.template.Loader
构造函数,可以进行全局的转义开关设置,也可以使用{% autoescape None %}
指令的模板文件或在单个表达式中使用{% raw ...%}
替换{{...}}
达到关闭转义的目的。另外,在所有位置中都可以使用转义函数的名称代替None
。
请注意,虽然Tornado的自动转义有助于避免XSS漏洞,但不能保证在所有的情况下都有用。出现在某些位置的表达式(例如Javascript或CSS)可能需要额外的转义。此外,必须注意始终在可能包含不可信内容的HTML属性中使用双引号和xhtml_escape,或者必须为属性使用单独的转义函数(可在http://wonko.com/post/html-escaping中查看示例)。
国际化
当前用户的区域设置(无论它们是否登录)始终在请求处理程序中以self.locale
的形式提供,在模板中始终以locale
的形式提供。语言环境的名称(例如en_US
)储存在locale.name
中,您可以使用Locale.translate
方法翻译字符串。模板还有_()
这样可用于字符串转换的全局函数调用。 translate函数有两种形式:
_("Translate this string")
它根据当前语言环境直接翻译字符串,并且:
_("A person liked this", "%(num)d people liked this",
len(people)) % {"num": len(people)}
它根据第三个参数的值翻译一个可以是单数或复数的字符串。在上面的示例中,如果len(people)
为1,则将返回第一个字符串的翻译,否则将返回第二个字符串的翻译。
下面是一个恰当的国际化模板:
<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>
默认情况下,我们使用用户浏览器发送的Accept-Language
标头检测用户的语言环境。如果我们找不到合适的Accept-Language
值,我们选择en_US
。如果你允许用户将其区域设置设置为首选项,则可以通过重写RequestHandler.get_user_locale
来替换此默认区域设置选择:
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
及其相关工具使用的.mo
格式,还有简单的.csv
格式。 应用程序通常会在启动时调用tornado.locale.load_translations
或tornado.locale.load_gettext_translations
; 有关支持的格式的详细信息,请参阅这些方法。
您可以使用tornado.locale.get_supported_locales()
获取应用程序中所支持的语言环境列表。 将在支持的语言环境列表中选择最接近的匹配项作为用户的语言环境。例如,如果用户的语言环境是es_GT
,并且支持es
语言环境,那么在该请求中,self.locale
的值就是es
。如果找不到接近的匹配项,我们会切换回en_US
。
UI模块
Tornado支持UI模块(UI modules),这将使你的应用可以更轻松地支持标准化、可复用的UI组件。 UI模块就像用渲染页面组件的特殊函数调用一样,它们可以与自己的CSS和JavaScript打包在一起。
例如,如果你想要自己写一个博客,并且希望在博客主页和每个博客条目页面上都显示博客条目,则可以创建一个Entry
模块以在所有页面上渲染它们。首先,为UI模块创建一个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)
在应用程序中通过ui_modules
设置告诉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 %}
语句调用模块。例如,你可以从home.html
调用Entry
模块:
{% for entry in entries %}
{% module Entry(entry) %}
{% end %}
以及从entry.html
中调用:
{% module Entry(entry, show_comments=True) %}
模块可以通过重写embedded_css
,embedded_javascript
,javascript_files
或css_files
方法来包含自定义CSS和JavaScript函数:
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)
无论在页面上使用模块多少次,CSS和JavaScript模块都将包含在内。 CSS始终包含在页面的<head>
中,并且JavaScript总是包含在页面末尾的</body>
标记之前。
当不需要额外的Python代码时,模板文件本身可以用作模块。例如,可以重写前面的示例以将以下内容放在module-entry.html
中:
{{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }}
<!-- more template html... -->
将使用下面命令调用重写后的模板模块:
{% module Template("module-entry.html", show_comments=True) %}
set_resources
函数仅在通过{% module Template(...) %}
调用的模板中可用。与{% include ... %}
指令不同,模板模块与其包含模板具有不同的命名空间——它们只能看到全局模板命名空间和它们自己的关键字参数。
上一篇:一个Tornado网络应用的结构
下一篇:认证与安全