前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Tornado入门(六)【模板和UI】

Tornado入门(六)【模板和UI】

作者头像
用户2936342
发布2018-08-27 14:18:51
9500
发布2018-08-27 14:18:51
举报
文章被收录于专栏:nummynummy

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=Falsedebug=True

模板语法

Tornado模板语言实际上就是嵌套了Python流程控制语句和表达式的HTML文本。

代码语言:javascript
复制
<html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
     <ul>
       {% for item in items %}
         <li>{{ escape(item) }}</li>
       {% end %}
     </ul>
   </body>
 </html>

如果将上面的模板存储为template.html,并与py文件存放在同一目录,渲染过程如下:

代码语言:javascript
复制
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现在支持iffor, whiletry,这些语句都以{% end %}结尾。Tornado同样支持模板继承extends和块block语句。具体参考tornado.template

模板中的表达式可以为任意的Python表达式,包括函数调用。模板中的代码在一个命名空间中执行,这个命名空间包括了如下对象和函数。

当我们创建系统应用时,需要利用到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方法翻译字符串。模板中也可以使用全局函数_()

翻译函数有两种形式:

代码语言:javascript
复制
_("Translate this string")

基于当前的locale对象翻译字符串。

代码语言:javascript
复制
_("A person liked this", "%(num)d people liked this",
  len(people)) % {"num": len(people)}

提供第三个参数,将字符串翻译成单数形式或者复数形式。

翻译过程中最为常用的还是Python的格式化字符串。

下面是一个模板示例:

代码语言:javascript
复制
<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方法:

代码语言:javascript
复制
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模块支持以下两种格式的翻译文件:

  • .mo格式文件
  • .csv格式文件

在启动应用的时候,分别通过tornado.locale.load_gettext_translationstornado.locale.load_translations进行加载。

通过tornado.locale.get_supported_locales()可以获取所有支持的语言,tornado会基于用户的设置选择最匹配的语言。

UI模块

Tornado支持UI模块,以便支持标准的,可重用的UI组件。UI模块是特殊的函数,用于渲染页面的组件,而且它们可包含自己的CSSJavascript文件。

例如,如果你想正在实现一个博客系统,你希望博客入口同时出现在博客主页和每篇博客的页面,这时可以创建一个Entry模块,然后在每个页面都进行渲染,首先创建UI模块uimodules.py:

代码语言:javascript
复制
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

代码语言:javascript
复制
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中:

代码语言:javascript
复制
{% for entry in entries %}
  {% module Entry(entry) %}
{% end %}

entry.html中:

代码语言:javascript
复制
{% module Entry(entry, show_comments=True) %}

模块中还可以包含CSSJavascript信息,重写embedded_cssembedded_javascriptjavascript_files或者css_files即可。

代码语言: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)

模块中的CSSJavascript只会加载一次,CSS包含在<head>中,Javascript位于在</body>前面。

当不需要格外的Python代码时,模板文件本身也可以作为一个模块。例如,前面的例子可以重写为module.entry.html

代码语言:javascript
复制
{{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }}
<!-- more template html... -->

修正之后的模块调用如下:

代码语言:javascript
复制
{% module Template("module-entry.html", show_comments=True) %}

set_resources函数只能通过{% module Template(...) %}来调用。跟{% include %}不一样,模板模块拥有一个独立的命名空间。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.09.30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 配置模板
  • 模板语法
  • 国际化
  • UI模块
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档