当前位置: 首页 > 文档资料 > Tornado 用户手册 >

模版和 UI

优质
小牛编辑
142浏览
2023-12-01

Tornado 包含了一个简单, 快速, 灵活的模版语言. 这章节也描述了与语言相关的国际化问题.

Tornado 也可以使用其它的 Python 模版语言, 虽然没有将这些系统的整合到 中. 而是简单的将模版转换成字符串发送给

设置模版

默认情况下, Tornado 会寻找在当前 .py 文件相同目录下的所关联的模版文件. 如果要将模版文件放到另外一个目录中, 使用 template_path (或者覆盖 如果你在不同的处理程序中有不同的模版).

如果要从非文件系统路径加载模版, 在子类 中配置设置 template_loader .

被编译过的模版默认时被缓存的; 要关闭缓存使得每次每次对于文件的改变都是可见的, 使用应用程序设置 compiled_template_cache=False 或者 debug=True.

模版语法

Tornado 模本文件仅仅是一个 HTML (或者其他基于文本的文件格式) 附加 Python 控制语句和内建的表达式:

<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 模版支持 控制语句 (control statements)表达式 (expressions) . 控制语句被 {% and %} 包裹着, 例如., {% if len(items) > 2 %}. 表达式被 {{}} 围绕, 再例如., {{ items[0] }}.

模版中的控制语句多多少少与 Python 中的控制语句相映射. 我们支持 if, for, while, 和 try, 所有这些都包含在 {%  %} 之中. 我们也支持 模板继承 使用 extendsblock 语句, 详见 .

表达式可以时任何的 Python 表达式, 包括函数调用. 模版代码可以在以下对象和函数的命名空间中被执行. (注意这个列表可用在 和 . 如果你直接在 外使用 模块, 下面许多别名是不可用的).

  • escape: 的别名
  • xhtml_escape: 的别名
  • url_escape: 的别名
  • json_encode: 的别名
  • squeeze: 的别名
  • linkify: 的别名
  • datetime: Python 模块
  • handler: 目前的 对象
  • request: 的别名
  • current_user: 的别名
  • locale: 的别名
  • _: 的别名
  • static_url: 的别名
  • xsrf_form_html: 的别名
  • reverse_url: 的别名
  • 所有 ui_methodsui_modulesApplication 设置
  • 所有传递给 或者 的参数

当你真正创建一个应用程序时, 你可能会去查看所有 Tornado 模版的特性, 特别时模版继承. 这些内容详见 部分 (某些特性, 包括 UIModules 在 模块中描述)

在引擎下, Tornado 模版被直街翻译成 Python. 在你模版文件中的表达式将会被翻译成 Python 函数来代表原来的模版; 我们不在模版语言中阻止任何东西; 我们创造它的目的时为了提供更灵活的特性, 而不是有严格限制的模版系统. 因此, 如果你在你的模版文件中随意写入了表达式, 你再执行时将会得到相依随机的错误.

默认情况下, 所有模版文件的输出将会被 方法转义. 这个设置可以通过给 传递全局参数 autoescape=None 或者使用 构造器进行修改, 或者在模版文件中检测到 {% autoescape None %} , 或者简单的将 {{ ... }} 替换成 {% raw ...%} 的表达式. 此外, 可以在设置这些地方的转义函数为 None 已达到相同的效果.

注意, 尽管 Tornado’s 的自动转义在防止 XSS 漏洞上是有帮助的, 但是不能适用于所有的情况. 出现在适当位置的表达式, 例如 Javascript 或者 CSS, 可能需要额外的转义. 此外, 必须要额外注意使用在 HTML 中使用双括号和 中包含一些不可信的内容, 或者在属性中使用单独的转义函数 (查看示例. http://wonko.com/post/html-escaping)

国际化

目前用户的位置 (不论用户是否登陆) 在请求处理程序中的 self.locale 和 模版中的 locale 都是可用的. 位置的名字 (例如, en_US) 在 locale.name 中是可用的, 你也可以通过 方法来翻译字符串. 模版中也有一个全局函数叫做 _() 用来翻译字符串. 翻译函数有两种形式:

_("翻译这段文字")

这将会根据用户的位置直接翻译, 还有:

_("A person liked this", "%(num)d people liked this",
  len(people)) % {"num": len(people)}

可以根据第三个参数的数量来决定单复数形式. 在以上的例子中, 第一个翻译将会在 len(people)1 时被激活, 在其它情况下会激活第二个翻译.

大多是翻译时利用 Python 中的变量占位符 ( 前面例子中的 %(num)d ) 占位符在翻译时可以被替换.

这是一个正确的国际化模版:

<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 作为 Accept-Language 的值. 如果你希望用户自己设定自己的位置, 你可以通过修改默认选项 来实现:

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 头部来确定.

模块支持两种格式的翻译: 一种使用 getttext 和有关工具的 .mo 格式, 另一种时简单的 .csv 格式. 应用程序将会在启动时调用 或者 ; 查看这些支持格式方法来获取更详细的信息.

你可以通过调用方法 来查看支持的地理位置. 用户的位置将会基于它所在的最近位置. 例如, 用户的位置是 es_GT , es 是支持的, self.locale 对那个请求将会设置为 es . 但如果勋章寻找失败 en_US 将会作为默认设置.

UI 模版

Tornado 支持 UI 模版 为了更加简单的支持标准, 在你的程序中重用 UI 组件. UI 模块就像特殊的方法调用一样用来显示页面上的组件, 它们也可以被报装在 CSS 和 JavaScript 中.

例如, 如果你正在实现一个博客, 你想把博客的入口同时放置在主页和每一页的入口, 你可以定义一个 Entry 模块来实现它们. 首先, 创建一个 Python 模块当作一个 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)

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) %}

模块可以通过覆盖包含定制的 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 ... %} 指令, 模版模块在模版容器中有一个不同的命名空间 - 它们只能看到全局模版的命名空间和自己的关键字参数.