翻譯:introduce to tornado - form and template

在上一章節中,咱們看到了如何使用tornado去建立和配置一個簡單的web應用。咱們學習了:handlers、http方法和tornado的總體框架結構。在這個章節,咱們將要開始學習如何在web應用中使用更多更強大的功能。 css

和大部分web的框架同樣,tornado設計的其中一個目標就是幫助你經過tornado更快的完成應用程序,實現代碼的高可用和整潔。tornado很是靈活,它幾乎支持全部的模板語言,它包括了一個輕量級、快速、靈活的模板。 html

簡單的例子Poem Maker Pro python

讓咱們經過這個名爲Poem Maker Pro的例子開始吧!Poem Maker Pro 是一個web應用,它會經過一個html表格去接收用戶填寫的東西。而且將結果從新在網頁中顯示出來。 web

例2-1 poemmaker.py json

Code    View Copy Print
  1. import os.path   
  2. import tornado.httpserver   
  3. import tornado.ioloop   
  4. import tornado.options   
  5. import tornado.web   
  6. from tornado.options import define, options   
  7. define(「port」, default=8000, help=」run on the given port」, type=int)   
  8. class IndexHandler(tornado.web.RequestHandler):   
  9.     def get(self):   
  10.         self.render(‘index.html’)   
  11. class PoemPageHandler(tornado.web.RequestHandler):   
  12.     def post(self):   
  13.         noun1 = self.get_argument(‘noun1′)   
  14.         noun2 = self.get_argument(‘noun2′)   
  15.         verb = self.get_argument(‘verb’)   
  16.         noun3 = self.get_argument(‘noun3′)   
  17.         self.render(‘poem.html’, roads=noun1, wood=noun2, made=verb,   
  18.             difference=noun3)   
  19. if __name__ == ’__main__‘:   
  20.     tornado.options.parse_command_line()   
  21.     app = tornado.web.Application(   
  22.         handlers=[(r'/', IndexHandler), (r'/poem', PoemPageHandler)],   
  23.         template_path=os.path.join(os.path.dirname(__file__), 」templates」)   
  24.     )   
  25.     http_server = tornado.httpserver.HTTPServer(app)   
  26.     http_server.listen(options.port)   
  27.     tornado.ioloop.IOLoop.instance().start()  

除了poemmaker.py以外,你還須要使用子目錄templates中的兩個文件例2-二、例2-3。 api

例2-2 index.html 瀏覽器

<!DOCTYPE html><html> 緩存

<head><title>Poem Maker Pro</title></head>
<body>
<h1>Enter terms below.</h1>
<form method=」post」 action=」/poem」>
<p>Plural noun<br><input type=」text」 name=」noun1″></p>
<p>Singular noun<br><input type=」text」 name=」noun2″></p>
<p>Verb (past tense)<br><input type=」text」 name=」verb」></p>
<p>Noun<br><input type=」text」 name=」noun3″></p>
<input type=」submit」>
</form>
</body>
</html> 服務器

例2-3 poem.html 併發

 

<!DOCTYPE html>
<html>
<head><title>Poem Maker Pro</title></head>
<body>
<h1>Your poem</h1>
<p>Two {{roads}} diverged in a {{wood}}, and I&mdash;<br>
I took the one less travelled by,<br>
And that has {{made}} all the {{difference}}.</p>
</body>
</html>

你能夠經過下面的命令運行這個程序:

  1. $ python poemmaker.py –port=8000  

如今咱們能夠在瀏覽器的地址填上http://localhost:8000 。當瀏覽器打開URL時,首先請求的是根目錄(/),在tornado程序中,將會經過渲染index.htm;將首頁顯示出來。

reilly introduction to tornado figure 2-1.jpg

這個表格包括了一些文本框(named noun1 , noun2 , 等等),文本框的內容在用戶點擊 Submit 按鈕以後將會經過POST 的方式發送給 /poem,如今請填寫這個表單並點擊Submit吧。

發送POST請求以後,tornado應用將會調用poem.html,將你輸入表格的數據讀取到變量中,而後把結果傳入到poem.html預置的接口中顯示出來。

reilly introduction to tornado figure 2-2.jpg

 template_path變量定義了tornado將要到哪裏去調用模板文件,咱們將會在這一章和第三章深刻的去了解模板文件的特性和語法,請記住最基本的一點是:HTML模板文件容許你嵌入python代碼。上面的代碼告訴python去調用tornado應用目錄下模板目錄中的HTML文件。

一旦咱們告訴tornado哪裏能夠找到模板,咱們就能夠經過RequestHandler類中的render方法去告訴tornado,讀取模板文件,將變量傳入模板中的嵌入代碼中執行,並將熱鍋發送給瀏覽器,在IndexHandler,對於每個例子咱們均可以找到:

Self.render(‘index.html’)

這個代碼將會告訴tornado在templates目錄中尋找並調用index.html,讀取它裏面的內容,併發送給瀏覽器。

傳值

index.html就是一個模板,你看它只有一些基本的HTML標籤。可是更多時候咱們但願可以將咱們程序中的參數傳送給HTML模板。就像poem.html模板同樣,將PoemPageHandler中的參數提取出來在客戶端顯示。這是一個很是好的例子,讓咱們來看看它是如何進行工做的。

在poem.html 你能夠看到有一些使用雙重大括號嵌入的字符串{{and}}在模板中,像這樣:

<p>Two {{roads}} diverged in a {{wood}}, and I&mdash;<br/>
I took the one less travelled by,<br>
And that has {{made}} all the {{difference}}.</p>

這個在雙重大括號中嵌入的字符串是佔位符,當模板被調用的時候,咱們能夠用實際的變量去替換它。咱們能夠經過render指定哪些關鍵的參數會經過佔位符插入到HTML中,這些關鍵的參數的變量名與佔位符是一致的。這裏是PoemPageHandler中有關變量的代碼:

Code    View Copy Print
  1. noun1 = self.get_argument(‘noun1′)   
  2. noun2 = self.get_argument(‘noun2′)   
  3. verb = self.get_argument(‘verb’)   
  4. noun3 = self.get_argument(‘noun3′)   
  5. self.render(‘poem.html’, roads=noun1, wood=noun2, made=verb, difference=noun3)  

在這裏,咱們告訴模板須要使用變量noun1(它的值是來經過get_argument 方法從form表單中獲取的)替換模板中的roads佔位符,noun2的值替換模板中的wood佔位符,等等。咱們假定用戶分別在form表格中輸入了pineapples,grandfather clock,irradiated和supernovae。那麼返回的HTML結果應該是這樣的:

<p>Two pineapples diverged in a grandfather clock, and I&mdash;<br>
I took the one less travelled by,<br>
And that has irradiated all the supernovae.</p>

模板的語法

如今讓咱們來看一個簡單的例子,去了解一些模板語法的細節。模板在tornado裏面用一個簡單的文本標識符將python的表達式和控制語句標識出來。這些語句在tornado的模板中很是簡潔,你若是熟悉Django, Liquid或similar,就會發現tornado有不少與他們類似的地方,很是容易配置。在Poem Maker Pro這個例子中,咱們已經展現瞭如何使用在web應用中使用render方法發送HTML給瀏覽器,你能夠嘗試經過導入tornado應用的template 模塊到python解釋器中,直接輸出模板內容瞭解template模塊的功能。

Code    View Copy Print
  1. >>> from tornado.template import Template   
  2. >>> content = Template(「<html><body><h1>{{ header }}</h1></body></html>」)   
  3. >>> print content.generate(header=」Welcome!」)   
  4. <html><body><h1>Welcome!</h1></body></html>  

插入表達式

在例子2-1中,咱們使用大括號去嵌入了一些python的表達式到模板中,你可使用雙重大括號嵌入任意的python表達式,tornado將會自動將表達式的值插入到輸出中,下面是一些例子:

Code    View Copy Print
  1. >>> from tornado.template import Template   
  2. >>> print Template(「{{ 1+1 }}」).generate()   
  3. 2   
  4. >>> print Template(「{{ ’scrambled eggs’[-4:] }}」).generate()   
  5. eggs   
  6. >>> print Template(「{{ ’, ’.join([str(x*x) for x in range(10)]) }}」).generate()   
  7. 0, 1, 4, 9, 16, 25, 36, 49, 64, 81  

控制語句

你還能夠在tornado模板中使用python的控制語句和循環。控制語句使用{%和%}包圍起來,相似下面的例子:

{% if page is None %} 或{% if len(entries) == 3 %}

控制語句對於在大部分都和python語法相同,tornado支持 :if, for, while, try 。每個語句都從{%開始到%}結束。

因此相似下面的模板:

<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>{{ header }}</h1>
<ul>
{% for book in books %}
<li>{{ book }}</li>
{% end %}
</ul>
</body>
</html>

咱們若是在tornado的類中能夠這麼處理:

Code    View Copy Print
  1. class BookHandler(tornado.web.RequestHandler):   
  2.     def get(self):   
  3.         self.render(   
  4.         」book.html」,   
  5.         title=」Home Page」,   
  6.         header=」Books that are great」,   
  7.         books=[   
  8.             "Learning Python",   
  9.             "Programming Collective Intelligence",   
  10.             "Restful Web Services"   
  11.         ]   
  12.     )  

最後處理生成的是下面的html代碼

Code    View Copy Print
  1. <html>  
  2.     <head>  
  3.         <title>Home Page</title>  
  4.     </head>  
  5.     <body>  
  6.         <h1>Books that are great</h1>  
  7.         <ul>  
  8.             <li>Learning Python</li>  
  9.             <li>Programming Collective Intelligence</li>  
  10.             <li>Restful Web Services</li>  
  11.        </ul>  
  12.     </body>  
  13. </html>  

tornado的模板與其它python模板系統比起來,對使用表達式和塊函數沒有任何限制,因此你能夠在你的模板中執行完整的python代碼。

你還能夠經過{% set foo = ‘bar’ %} 這樣的形式在控制塊中設置參數,還能夠在控制塊中使用更多的語句。可是在大多數狀況下,咱們建議你最好使用UI模塊去實現複雜的功能,後面咱們將會了解更多關於UI模塊的細節。

在模板中插入函數

tornado對於模板默認設置了一些有用的功能,他們包括:

escape(s)

    用響應的HTML實體去替換字符串中的&<>

url_escape(s)

    使用urllib.quote_plus去替換URL編碼中的一些特性字符

json_encode(val)

    將變量經過json傳遞(它將會調用底層的json庫去轉換存儲的數據,你能夠去查找相關的文檔瞭解這個函數接收的參數和返回的內容)

squeeze(s)

     過濾字符串s,用一個空格替換多個空格序列。

tornado1.x的版本中的templates類沒有autoescaping功能,在tornado2.0中autoescaping是自動啓用的(能夠經過autoescape = None關閉這個構造函數),以實現代碼的向後兼容性。

一個完整的例子:The Alpha Munger

在例子2-4中,咱們將會把以前全部章節學習到的東西放到一塊兒,這個應用被稱爲 The Alpha Munger,用戶輸入兩個文本:source 和 replacement 。應用將會返回一個副本,使用source將與replacement中首字母相同的單詞替換掉。圖2-3展現了輸入的表單,圖2-4顯示的是生成的文本。

這個應用包括四個文件:main.py(tornado主程序),style.css(一個css樣式表),index.html和munged.html(tornado模板),讓咱們來看一看這些代碼:

例子2-4 main.py

Code    View Copy Print
  1. import os.path   
  2. import random  
  3. import tornado.httpserver   
  4. import tornado.ioloop   
  5. import tornado.options   
  6. import tornado.web   
  7. from tornado.options import define, options   
  8. define(「port」, default=8000, help=」run on the given port」, type=int)   
  9. class IndexHandler(tornado.web.RequestHandler):   
  10.     def get(self):   
  11.         self.render(‘index.html’)   
  12. class MungedPageHandler(tornado.web.RequestHandler):   
  13.     def map_by_first_letter(self, text):   
  14.         mapped = dict()   
  15.         for line in text.split(‘\r\n’):   
  16.             for word in [x for x in line.split(' ') if len(x) > 0]:   
  17.                 if word[0] not in mapped: mapped[word[0]] = []   
  18.                 mapped[word[0]].append(word)   
  19.         return mapped   
  20.     def post(self):   
  21.         source_text = self.get_argument(‘source’)   
  22.         text_to_change = self.get_argument(‘change’)   
  23.         source_map = self.map_by_first_letter(source_text)   
  24.         change_lines = text_to_change.split(‘\r\n’)   
  25.         self.render(‘munged.html’, source_map=source_map, change_lines=change_lines,   
  26.         choice=random.choice)   
  27. if __name__ == ’__main__‘:   
  28.     tornado.options.parse_command_line()   
  29.     app = tornado.web.Application(   
  30.         handlers=[(r'/', IndexHandler), (r'/poem', MungedPageHandler)],   
  31.         template_path=os.path.join(os.path.dirname(__file__), 」templates」),   
  32.         static_path=os.path.join(os.path.dirname(__file__), 」static」),   
  33.         debug=True  
  34.     )   
  35.     http_server = tornado.httpserver.HTTPServer(app)   
  36.     http_server.listen(options.port)   
  37.     tornado.ioloop.IOLoop.instance().start()  
reilly introduction to tornado figure 2-3.jpg

圖片2-3

reilly introduction to tornado figure 2-4.jpg

圖片2-4

 

請注意static_path參數是如何在應用中構造的,咱們將會對其作更詳細的解釋,如今你只須要知道,static_path參數將會指定一個靜態資源(若是圖片、CSS文件、JavaScript文件等等)的目錄給應用程序使用。你還須要指定一個靜態目錄存放index.html 和munged.html(在例2-五、例2-6中)。

例子2-5 index.html

Code    View Copy Print
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <link rel=「stylesheet」 href=」{{ static_url(「style.css」) }}」>  
  5.         <title>The Alpha Munger</title>  
  6.     </head>  
  7.     <body>  
  8.         <h1>The Alpha Munger</h1>  
  9.         <p>Enter two texts below. The replacement text will have its words   
  10.         replaced by words beginning with the same letter in the source text.</p>  
  11.         <form method=「post」 action=「/poem」>  
  12.             <p>Source text<br>  
  13.             <textarea rows=4 cols=55 name=「source」></textarea></p>  
  14.             <p>Text for replacement<br>  
  15.             <textarea rows=4 cols=55 name=「change」></textarea></p>  
  16.         <input type=「submit」>  
  17.         </form>  
  18.     </body>  
  19. </html>  

 

例子2-6  munged.html

Code    View Copy Print
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <link rel=「stylesheet」 href=」{{ static_url(「style.css」) }}」>  
  5.         <title>The Alpha Munger</title>  
  6.     </head>  
  7.     <body>  
  8.         <h1>Your text</h1>  
  9.         <p>  
  10.     {% for line in change_lines %}   
  11.         {% for word in line.split(‘ ’) %}   
  12.             {% if len(word) > 0 and word[0] in source_map %}   
  13.                 <span class=「replaced」  
  14.                     title=「{{word}}」>{{ choice(source_map[word[0]]) }}</span>  
  15.             {% else %}   
  16.                 <span class=「unchanged」 title=「unchanged」>{{word}}</span>  
  17.             {% end %}   
  18.         {% end %}   
  19.                 <br>  
  20.     {% end %}   
  21.             </p>  
  22.     </body>  
  23. </html>  

 

最後讓咱們來看看例子2-7 style.css的內容,而且把它放到命名爲static的子目錄中(咱們稍後會討論爲何使用static做爲子目錄)

例子2-7 style.css

  1. body {   
  2.     font-family: Helvetica,Arial,sans-serif;   
  3.     width: 600px;   
  4.     margin: 0 auto;   
  5. }   
  6. .replaced:hover { color: #00f; }  

 如何運行

這個tornado應用定義了兩個Handler類處理請求,IndexHandler 和MungedPageHandler, IndexHandler類知識簡單地將包含一個容許用戶經過post提交的表單模板index.html傳送給瀏覽器,當用戶提交的時候,將會把表單傳送給後臺並跳轉到/poem。

MungedPageHandler類設置了一個POST類去匹配/poem,當請求到達的時候,它將會執行類中的一些語句,並將結果嵌入到調用的模板munged.html返回給瀏覽器。在map_by_first_letter方法中切分輸入的source文本(表單中填寫的值),而後按照單詞的首字母去建立一個與source關聯的文本(咱們將會經過source_map去調用它),這個字典將會傳送給munged.html模板,用戶指定做爲替換的文本munged(贊成是從form表單中獲取的),咱們將會經過python的一個標準庫random.choice,隨機生成一些數值,將列表中的隨機元素提取出來。

在munged.html,咱們將會遍歷每一行,若是發現當前的單詞首字母與source_map中返回的元素相同,咱們就會使用random.choice去獲取一個隨機的單詞,將munged中的單詞替換掉。每個單詞都會包含一個tag,用於標註這個單詞是否是已經被替換了(class=」replaced」)或(class=」unchanged」),咱們還會設置一個tag,當用戶的鼠標移動到這個替換後的詞上面時能夠看到原先它是哪一個單詞的。你能夠在圖2-5中看到這個效果

圖2-5

reilly introduction to tornado figure 2-5.jpg

在這個例子中,你可能已經注意到 debug = True它是調用了一個方便調試的測試模式,tornado.autoreload模塊,當python文件的內容發生改變時,tornado將會重啓服務器,每一次更新模板都會致使服務重啓,它適用於一個須要常常更新或改動的tornado應用,可是不要把它放到生產環境中,由於它會阻止tornado去生成緩存模板。

靜態文件

當咱們編寫web應用的時候,你可能要常用 static content , style-sheets, JavaScript file, Images 這樣的靜態文件,tornado提供了一個很簡潔的方式來幫助咱們管理靜態文件

設置靜態static_path

你能夠經過template_path將static_path的目錄做爲變量傳遞給應用程序,這裏是Alhpa Munger 中關於設置靜態目錄的一小段代碼:

Code    View Copy Print
  1. app = tornado.web.Application(   
  2.     handlers=[(r'/', IndexHandler), (r'/poem', MungedPageHandler)],   
  3.     template_path=os.path.join(os.path.dirname(__file__), 」templates」),   
  4.     static_path=os.path.join(os.path.dirname(__file__), 」static」),   
  5.     debug=True  
  6. )  

在這裏咱們設置static_path變量的子目錄爲static,如今應用程序接收到相似/static/filename.ext的請求時,將會經過訪問static目錄讀取filename.ext文件的方式將文件讀取並返回給客戶端.

便準的靜態URL static_url

在tornado的template 模塊中,咱們經過調用static_url的方式到靜態目錄static中訪問文件。讓咱們看看index.html是怎麼調用static_url的。

Code    View Copy Print
  1. <link rel=「stylesheet」 href=」{{ static_url(「style.css」) }}」>  

爲何要在模板中用static_url去取代硬編碼訪問靜態文件呢?

這裏有兩個理由:

  1. static_url函數會基於靜態目錄的文件內容建立一個hash,並將文件的內容以v 參數的形式添加到查詢字符串中。這個hash可以保證瀏覽器載入的永遠都是最新的版本而不是以前緩存的版本,這樣不管對於開發仍是部署到生產環境都很是有用,由於用戶不須要清理瀏覽器的緩存就能夠看到你修改的靜態化內容
  2. 當你調整你的應用程序目錄的時候,不須要去改動你模板中的代碼,例如,你能夠配置tornado使用/s做爲新的靜態目錄,你就能夠簡單的經過修改/static 成/s的形式,static_url將會自動將模板中的url更新,假如你使用的是硬編碼的方式將靜態文件寫到你的源文件中,你就必需要手動去修改每個模板

下一步:模板

如今你應該已經掌握了tornado中 template系統的基本功能,對於大部分簡單的web應用來講,好比Alpha Munger這已經夠用了,可是咱們尚未將template的內容學習完,在template中組建塊和模塊還有好幾個很是巧妙的應用,塊和模塊這兩個特性將有效地幫助咱們編寫和維護更復雜的web應用,讓咱們在第三章瞭解這些特性吧。

原創翻譯,首發地址:http://blog.xihuan.de/tech/web/tornado/tornado_form_and_templates.html


上一篇:翻譯:introduce to tornado - a simple example

下一篇:翻譯:introduce to tornado - Extending Templates

相關文章
相關標籤/搜索