翻译: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

相关文章
相关标签/搜索