Tornado提供了一个简单,快速,灵活的模板引擎。
Tornado也可以使用其他任意的模板引擎, 尽管并没有明确规则如何在RequestHandler.render整合进这些引擎。实际上只需要将模板渲染成字符串,然后传递给RequestHadler.write方法即可。
默认情况下,Tornado会在跟Python文件相同的目录下查找模板文件。如果需要将模板文件放在单独的路径,可以通过Application setting中的template_path 进行配置,如果是要求不同处理器的模板不一样,则可以重写RequestHandler.get_tempplate_path方法。
如果需要从非文件系统中加载模板,则可以继承tornado.template.BaseLoader然后传递它的一个实例给application配置的template_loader参数。
默认会缓存编译之后的模板,如果需要取消缓存,则需要设置参数compiled_template_cached=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,并与py文件存放在同一目录,渲染过程如下:
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类似,Tornado现在支持if, for, while和try,这些语句都以{% end %}结尾。Tornado同样支持模板继承extends和块block语句。具体参考tornado.template。
模板中的表达式可以为任意的Python表达式,包括函数调用。模板中的代码在一个命名空间中执行,这个命名空间包括了如下对象和函数。
escape: tornado.escape.xhtml_escape的别名xhtml_escape: tornado.escape.xhtml_escape的 别名url_escape: tornado.escape.url_escape的别名json_encode: tornado.escape.json_encode的别名squeeze: tornado.escape.squeeze 的别名linkify: tornado.escape.linkify 的别名datetime: Python datetime 模块handler: 当前 RequestHandler 对象request: handler.request的别名current_user: handler.current_user的别名locale: handler.locale的别名_: handler.locale.translate的别名static_url: handler.static_url的别名xsrf_form_html: handler.xsrf_form_html的别名reverse_url: Application.reverse_url的别名ui_methods 和 ui_modules Application 配置的所有入口render 或者 render_string的任意参数当我们创建系统应用时,需要利用到Tornado的很多特性,具体可以参考tornado.template的文档。
Tornado模板会被编译为Python代码,所有的模板输出默认都会使用tornado.escape.xhtml_escape转义,可以在应用的设置中通过参数autoescape=False来关闭转义,或者在tornado.template.Loader构造器中设置这个参数。如果是在模板文件中,可以使用{% autoescape None %} 指令。如果是表达式,则可以使用{% raw ...%}。
Tornado提供的自动转义可以避免XSS攻击,但是它并不能处理所有情况,例如Javascript和CSS中的表达式可能需要格外的转义。
当前用户的本地化信息保存在处理器的self.locale变量中,在模板中可以通过locale访问到。本地化的名称保存在locale.name中,例如en_US。可以使用Locale.translate方法翻译字符串。模板中也可以使用全局函数_()。
翻译函数有两种形式:
_("Translate this string")基于当前的locale对象翻译字符串。
_("A person liked this", "%(num)d people liked this",
len(people)) % {"num": len(people)}提供第三个参数,将字符串翻译成单数形式或者复数形式。
翻译过程中最为常用的还是Python的格式化字符串。
下面是一个模板示例:
<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来获取用户的语言信息,如果没有找到合适的,则使用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模块支持以下两种格式的翻译文件:
在启动应用的时候,分别通过tornado.locale.load_gettext_translations和tornado.locale.load_translations进行加载。
通过tornado.locale.get_supported_locales()可以获取所有支持的语言,tornado会基于用户的设置选择最匹配的语言。
Tornado支持UI模块,以便支持标准的,可重用的UI组件。UI模块是特殊的函数,用于渲染页面的组件,而且它们可包含自己的CSS和Javascript文件。
例如,如果你想正在实现一个博客系统,你希望博客入口同时出现在博客主页和每篇博客的页面,这时可以创建一个Entry模块,然后在每个页面都进行渲染,首先创建UI模块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)需要在应用配置中告诉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中:
{% for entry in entries %}
{% module Entry(entry) %}
{% end %}entry.html中:
{% module Entry(entry, show_comments=True) %}模块中还可以包含CSS和Javascript信息,重写embedded_css,embedded_javascript,javascript_files或者css_files即可。
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 %}不一样,模板模块拥有一个独立的命名空间。