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

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文本。

<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现在支持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方法翻译字符串。模板中也可以使用全局函数_()

翻译函数有两种形式:

_("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模块支持以下两种格式的翻译文件:

  • .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:

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

模块中还可以包含CSSJavascript信息,重写embedded_cssembedded_javascriptjavascript_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)

模块中的CSSJavascript只会加载一次,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 %}不一样,模板模块拥有一个独立的命名空间。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IT笔记

Illegal attempt to associate a collection with two open sessions

错误信息: org.springframework.orm.hibernate3.HibernateSystemException: Illegal attem...

41850
来自专栏Crossin的编程教室

【Git 第7课】 忽略文件

在文件夹中,经常会有些“其他”文件,比如上节课最后提到的,编辑器产生~结尾的备份文件,或者一些临时文件。又可能,某些文件我们只是在本地使用,并不想提交到远程的仓...

359100
来自专栏Linux运维学习之路

Linux下批量修改文件名方法

对于在Linux中修改文件名的方式一般我们会用mv命令进行修改,但是mv命令是无法处理大量文件修改名称。 但是在处理大量文件的时候该如何进行批量修改呢? 方法一...

30570
来自专栏磨磨谈

Cephfs的文件存到哪里了

在ceph里面使用rbd接口的时候,存储的数据在后台是以固定的prifix的对象存在的,这样就能根据相同的前缀对象去对image文件进行拼接或者修复

16230
来自专栏小勇DW3

使用Redis作为分布式锁的一些注意点

最简单的方法是使用setnx命令。key是锁的唯一标识,按业务来决定命名,value为当前线程的线程ID。

3.4K50
来自专栏破晓之歌

python之调用系统命令 原

os模块包装了不同操作系统的通用接口,使用户在不同操作系统下,可以使用相同的函数接口,返回相同结构的结果。

38940
来自专栏nummy

Tornado入门(五)应用结构

Tornado web应用的结构通常包含一个或者多个RequestHandler子类,一个将请求转发至处理器的Application对象,以及一个main()函...

12610
来自专栏北京马哥教育

Ansible 详细用法说明(二)

例:获取某台主机的变量 ansible 10.1.6.68 -m setup ===================================== sc...

38250
来自专栏好好学java的技术栈

“面试不败计划”:Java多线程和并发基础面试问答

多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应...

9120
来自专栏技术博客

Asp.Net Web API 2第五课——Web API路由

    Asp.Net Web API第一课——入门 http://www.cnblogs.com/aehyok/p/3432158.html

14150

扫码关注云+社区

领取腾讯云代金券