poemmaker.py:
# -*- coding:utf-8 -*- import os.path import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) #重定向到文件index.html class IndexHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') #读取数据并将数据重定向到poem.html中 class PoemPageHandler(tornado.web.RequestHandler): def post(self): noun1 = self.get_argument('noun1') noun2 = self.get_argument('noun2') verb = self.get_argument('verb') noun3 = self.get_argument('noun3') #将页面重定向到poem.html,并以字典的方式(roads=noun1等甲鱼rroads:noun1)进行传递,则poem.html中可用{{roads}}进行读取 self.render('poem.html', roads=noun1, wood=noun2, made=verb, difference=noun3) if __name__ == '__main__': #解析命令行 tornado.options.parse_command_line() app = tornado.web.Application( handlers=[(r'/', IndexHandler), (r'/poem', PoemPageHandler) ], #将模板路径修改未templates,则index.html和poem.html需保存在templates中 template_path = os.path.join(os.path.dirname(__file__), 'templates') ) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()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>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-<br> I took the one less travelled by, <br> And that has {{ made }} all the {{difference}}. </p> </body> </html>运行程序:
python poemmaker.py --port=8000
则浏览器操作及输出如下:
Tornado模板是被Python表达式和控制语句标记的简单文本文件:
备注:使用Ipython
In [1]: from tornado.template import Template In [2]: content = Template("<html><body><h1>{{ header }}</h1></body></html>") In [3]: print content.generate(header='Welcome!') <html><body><h1>Welcome!</h1></body></html>
我们可以将任何Python表达式放在双大括号中。Tornado将插入一个包含任何表达式计算结果值的字符串到输出中:
In [4]: from tornado.template import Template In [5]: print Template('{{ 1 + 1 }}').generate() 2 In [6]: print Template("{{ 'scrambled eggs'[-4:] }}").generate() eggs In [7]: print Template("{{', '.join([str(x * x) for x in range(10)]) }}").generate() 0, 1, 4, 9, 16, 25, 36, 49, 64, 81
我们同样可以在Tornado模板中使用Python条件和循环语句。控制语句以{%和%}包围,以{% end %}结尾,并以类似下面的形式被使用:
{% if page is None %} {% end %} {% for book in books %} {% end %}测试示例:
poemmaker.py:
# -*- coding:utf-8 -*- import os.path import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) #重定向到文件index.html class IndexHandler(tornado.web.RequestHandler): #post方法是从其他页面中读取数据进行处理,而get是从本页面中读取数据 def get(self): self.render('index.html', show=True, books=['book1', 'book2', 'book3', 'book4']) if __name__ == '__main__': #解析命令行 tornado.options.parse_command_line() app = tornado.web.Application( handlers=[(r'/', IndexHandler) ], #将模板路径修改未templates,则index.html需保存在templates中 template_path = os.path.join(os.path.dirname(__file__), 'templates') ) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()index.html:
<!DOCTYPE html> <html> <head><title>Books</title></head> <body> <form method='post' action='/poem'> {% if show %} {% for book in books %} <li>{{ book }}</li> {% end %} {% end %} </form> </body> </html>运行程序:
python poemmaker.py --port=8000浏览器显示如下:
Tornado在所有模板中默认提供了一些便利的函数:
escape(s): 替换字符串s中的&,<,>为他们对应的HTML字符。
url_encode(s): 使用urllib.quote_plus替换字符串s中的字符为URL编码形式。
json_encode(val): 将val编码成JSON格式。
squeeze(s): 过滤字符串s,把连续的多个空白字符替换成一个空格。
也可以使用自己编写的函数(函数名本身就是一个变量):
In [8]: from tornado.template import Template In [9]: def disemvowel(s): ...: return ''.join([x for x in s if x not in 'aeiou']) ...: In [10]: disemvowel('george') Out[10]: 'grg' In [11]: print Template("my name is {{ d('mortimer') }}").generate(d = disemvowel) my name is mrtmr
用户输入两个文本:一个“源”文本和一个“替代”文本。应用会返回替代文本的一个副本,并将其中每个单词替换成源文本中首字母相同的某个单词。
这个应用文件包括四个文件:main.py, style.css, index.html和munged.html:
main.cy:
# -*- coding:utf-8 -*- import os.path import random import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) #主文件:重定向到index.html class IndexHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') #处理页面的数据 class MungedPageHandler(tornado.web.RequestHandler): #将首字母相同的单词放在一个列表中 def map_by_first_letter(self, text): mapped = dict() for line in text.split('\r\n'): for word in [x for x in line.split(' ') if len(x) > 0]: if word[0] not in mapped: mapped[word[0]] = [] mapped[word[0]].append(word) return mapped #从其他页面中(index.html)中通过get_argument得到数据,经过Python罗辑处理(map_by_first_letter)后,将数据重定向到munged.html中 def post(self): source_text = self.get_argument('source') text_to_change = self.get_argument('change') source_map = self.map_by_first_letter(source_text) change_lines = text_to_change.split('\r\n') self.render('munged.html', source_map=source_map, change_lines=change_lines, choice=random.choice) if __name__ == '__main__': tornado.options.parse_command_line() app = tornado.web.Application( handlers = [(r'/', IndexHandler), (r'/poem', MungedPageHandler) ], #html文件放在templates中 template_path = os.path.join(os.path.dirname(__file__), 'templates'), #css文件放在static中 static_path = os.path.join(os.path.dirname(__file__), 'static'), debug = True ) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()index.html:
<!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="{{ static_url("style.css") }}"> <title>The Alpha Munger</title> </head> <body> <h1>The Alpha Munger</h1> <p>Enter two texts below. The replacement text will have its words replaced by words beginning with the same letter in the source text.</p> <form method="post" action="/poem"> <p>Source text<br> <textarea rows=4 cols=55 name="source"></textarea> </p> <p>Text for replacement<br> <textarea rows=4 cols=55 name="change"></textarea> </p> <input type="submit"> </form> </body> </html>
munged.html:
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="{{ static_url("style.css") }}"> <title>The Alpha Munger</title> </head> <body> <h1>Your text</h1> <p> {% for line in change_lines %} {% for word in line.split(' ') %} {% if len(word) > 0 and word[0] in source_map %} <span class = "replaced" title = "{{ word }}">{{ choice(source_map[word[0]]) }}</span> {% else %} <span class = "unchanged" title = "unchanged">{{ word }}</span> {% end %} {% end %} <br> {% end %} </p> </body> </html>style.css:
body { font-family: Helvetica, Arial, sans-serif; width: 600px; margin: 0 auto; } .replaced:hover { color: #00f;}
1. 使用static_url生成静态URL
Tornado模板模块提供了一个叫做static_url的函数来生成static目录下文件的URL:
<link rel="stylesheet" href="{{ static_url("style.css") }}">这个对static_url的调用生成了URL的值,并渲染输出类似下面的代码:
<link rel="stylesheet" href="/static/style.css?v=ab12">那为什么使用static_url而不是在模板中硬编码呢?有以下原因:
1) static_url函数创建了一个基于文件内容的hash值,并将其添加到URL(查询字符串的参数v)。这个hash值确保浏览器总是加载一个文件的最新版而不是之前的缓存版本。
2) 我们可以改变自己应用URL的结构,而不需要改变模板的代码。(比如之前定义css文件存放在static文件夹下,我们可以将css文件放在newStatic文件夹下,然后修改路径即可)
浏览器输入输出如下: