Tornado 4.3文檔翻譯: 用戶指南-模板和UI

譯者說

Tornado 4.3於2015年11月6日發佈,該版本正式支持Python3.5async/await關鍵字,而且用舊版本CPython編譯Tornado一樣可使用這兩個關鍵字,這無疑是一種進步。其次,這是最後一個支持Python2.6Python3.2的版本了,在後續的版本了會移除對它們的兼容。如今網絡上尚未Tornado4.3的中文文檔,因此爲了讓更多的朋友能接觸並學習到它,我開始了這個翻譯項目,但願感興趣的小夥伴能夠一塊兒參與翻譯,項目地址是tornado-zh on Github,翻譯好的文檔在Read the Docs上直接能夠看到。歡迎Issues or PR。javascript

模板和UI

Tornado 包含一個簡單,快速並靈活的模板語言. 本節介紹了語言以及相關的問題,好比國際化.css

Tornado 也可使用其餘的Python模板語言, 雖然沒有準備把這些系統整合到 RequestHandler.render 裏面. 而是簡單的將模板轉成字符串並傳遞給 RequestHandler.writehtml

配置模板

默認狀況下, Tornado會在和當前.py文件相同的目錄查找關聯的模板文件. 若是想把你的模板文件放在不一樣的目錄中, 可使用template_pathApplication setting (或複寫 RequestHandler.get_template_path 若是你不一樣的處理函數有不一樣的模板路徑).java

爲了從非文件系統位置加載模板, 實例化子類 tornado.template.BaseLoader併爲其在應用設置(application setting)中配置template_loader.python

默認狀況下編譯出來的模板會被緩存; 爲了關掉這個緩存也爲了使(對目標的)修改在從新加載後老是可見, 使用應用設置(application settings)中的compiled_template_cache=Falsedebug=True.git

模板語法

一個Tornado模板僅僅是用一些標記把Python控制序列和表達式嵌入HTML(或者任意其餘文本格式)的文件中:github

<html>
       <head>
          <title>{{ title }}</title>
       </head>
       <body>
         <ul>
           {% for item in items %}
             <li>{{ escape(item) }}</li>
           {% end %}
         </ul>
       </body>
     </html>

若是你把這個目標保存爲"template.html"而且把它放在你Python文件的相同目錄下, 你可使用下面的代碼渲染它:web

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).控制語句被包在{%%}中間, 例如,{% if len(items) > 2 %}. 表達式被包在{{}}之間, e.g.,{{ items[0] }}.express

控制語句或多或少都和Python語句相似. 咱們支持if,for,while, 和try, 這些都必須使用{% end %}來標識結束. 咱們也支持 模板繼承(template inheritance) 使用extendsblock標籤聲明, 這些內容的詳細信息均可以在 tornado.template 中看到.json

表達式能夠是任意的Python表達式, 包括函數調用. 模板代碼會在包含如下對象和函數的命名空間中執行 (注意這個列表適用於使用 RequestHandler.render
RequestHandler.render_string渲染模板的狀況. 若是你直接在RequestHandler以外使用tornado.template模塊, 下面這些不少都不存在).

  • 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_methodsui_modules`Application`設置的條目

  • 任何傳遞給 RequestHandler.renderRequestHandler.render_string 的關鍵字參數

當你正在構建一個真正的應用, 你可能想要使用Tornado模板的全部特性,尤爲是目標繼承. 閱讀全部關於這些特性的介紹在 tornado.template部分 (一些特性, 包括UIModules是在 tornado.web 模塊中實現的)

在引擎下, Tornado模板被直接轉換爲Python. 包含在你模板中的表達式會逐字的複製到一個表明你模板的Python函數中. 咱們不會試圖阻止模板語言中的任何東西; 咱們明確的創造一個高度靈活的模板系統, 而不是有嚴格限制的模板系統. 所以, 若是你在模板表達式中隨意填充(代碼), 當你執行它的時候你也會獲得各類隨機錯誤.

全部模板輸出默認都會使用 tornado.escape.xhtml_escape 函數轉義.這個行爲能夠經過傳遞autoescape=NoneApplication 或者tornado.template.Loader 構造器來全局改變, 對於一個模板文件可使用{% autoescape None %}指令, 對於一個單一表達式可使用{% raw ...%}來代替{{ ... }}. 此外, 在每一個地方一個可選的轉義函數名能夠被用來代替None.

注意, 雖然Tornado的自動轉義在預防XSS漏洞上是有幫助的, 可是它並不能勝任全部的狀況. 在某一位置出現的表達式, 例如Javascript 或 CSS, 可能須要另外的轉義. 此外, 要麼是必須注意老是在可能包含不可信內容的HTML中使用雙引號和 xhtml_escape , 要麼必須在屬性中使用單獨的轉義函數(參見 e.g. http://wonko.com/post/html-escaping)

國際化

當前用戶的區域設置(不管他們是否登陸)老是能夠經過在請求處理程序中使用self.locale或者在模板中使用locale進行訪問. 區域的名字(e.g.,en_US) 能夠經過locale.name得到, 你能夠翻譯字符串經過 Locale.translate 方法. 模板也有一個叫作_()全局函數用來進行字符串翻譯. 翻譯函數有兩種形式:

_("Translate this string")

是直接根據當前的區域設置進行翻譯, 還有:

_("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頭來發現用戶的區域設置. 若是咱們沒有發現恰當的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 modules使它易於支持標準, 在你的應用程序中複用UI組件. UI模塊像是特殊的函數調用來渲染你的頁面上的組件而且它們能夠包裝本身的CSS和JavaScript.

例如, 若是你實現一個博客, 而且你想要有博客入口出如今首頁和每篇博客頁, 你能夠實現一個Entry模塊來在這些頁面上渲染它們. 首先,爲你的UI模塊新建一個Python模塊, e.g.,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 %}語法調用一個模塊. 例如,你能夠調用Entry模塊從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 ... %}指令, 模板模塊有一個明確的命名空間它們的包含模板-它們只能看到全局模板命名空間和它們本身的關鍵字參數.

相關文章
相關標籤/搜索