这两天在看tornado user guide http://www.tornadoweb.org/en/stable/guide.html 中文翻译http://demo.pythoner.com/itt2zh/index.html
看完UI模块后就想用豆瓣的图书API来搞一个简单的图书介绍页面,也加深一下理解
一、创建一个web服务的入口文件 blockmain.py
#coding:utf-8 import tornado.web import tornado.httpserver import tornado.ioloop import tornado.options import os.path import json import urllib2 from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class MainHandler(tornado.web.RequestHandler): def get(self): self.render( "index.html", page_title = "Burt's Books ¦ Home", header_text = "Welcome to Burt's Books!", books = ['细说php','python','PHP','小时代'] ) class HelloModule(tornado.web.UIModule): def render(self): return'<h1>I am yyx and this is an information from module hello!</h1>' class BookModule(tornado.web.UIModule): def render(self,bookname): doubanapi = r'https://api.douban.com/v2/book/' searchapi = r'https://api.douban.com/v2/book/search?q=' searchurl = searchapi+bookname searchresult = urllib2.urlopen(searchurl).read() bookid = json.loads(searchresult)['books'][0]['id'] bookurl = doubanapi+bookid injson = urllib2.urlopen(bookurl).read() bookinfo = json.loads(injson) return self.render_string('modules/book.html',book = bookinfo) def embedded_javascript(self): return "document.write(\"hi!\")" def embedded_css(self): return '''.book {background-color:#F5F5F5} .book_body{color:red} ''' def html_body(self): return '<script>document.write("Hello!")</script>' if __name__ == "__main__": tornado.options.parse_command_line() app = tornado.web.Application( handlers = [ (r'/',MainHandler), ], template_path = os.path.join(os.path.dirname(__file__),'templates'), static_path = os.path.join(os.path.dirname(__file__),'static'), debug = True, ui_modules={'Hello':HelloModule,'Book':BookModule} ) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()
说明一下,一些基本的MVC概念
tornado也是通过pathinfo模式来匹配用户的输入来获得参数,然后再调用相应的处理函数,它是通过为各种匹配模式设定相应的class类来处理,比如我这里就是通过class MainHandler来处理来自/的get请求
MainHandler把请求render渲染到index.html,参数在index.html中通过{{参数}}来调用
二、建立相应的模板,先创建一个基础的父类main.html模板,创建templates目录,在它下面创建main.html,这个模板只是定义了最基础的网页框架,里面的具体内容由继承于它的子类来具体实现
<html> <head> <title>{{ page_title }}</title> <link rel="stylesheet" href="{{ static_url("css/style.css") }}" /> </head> <body> <div id="container"> <header> {% block header %}<h1>Burt's Books</h1>{% end %} </header> <div id="main"> <div id="content"> {% block body %}{% end %} </div> </div> <footer> {% set mailLink = '<a href="mailto:[email protected]">Contact Us</a>' %} {% set script = '<script>alert("hello")</script>' %} {% block footer %} <p> For more information about our selection, hours or events, please email us at{% raw mailLink %} <!-- {% raw script %} 这里将原样输出,也就是会弹一个框--> </p> {% end %} </footer> </div> <script src="{{ static_url("js/script.js") }}"></script> </body> </html>
这里是定义了一个主框架,其中里面的{% block header %}<h1>Burt's Books</h1>{% end %}是为了子类模板的继承的块(block),当子类继承了这个main.html,具体这个块里写什么内容由子类来实现,不实现的话就使用父类的默认 值,如是这里的<h1>Burt's Books</h1>,MainHandler类是render到一个index.html,那么接下来写一个index.html来继承这 个父类
{% extends "main.html" %} {% block header %} <h1>{{ header_text }}</h1> {% end %} {% block body %} <div id="hello"> <p>Welcome to Burt's Books!</p> {% module Hello() %} {% for book in books %} {% module Book(book) %} {% end %} <p>...</p> </div> {% end %}
简单简洁吧,这也是使用了继承的好处,不用再重复写父类的东西,只要实现父类的block内容即可
MainHandler类里的render方法中的参数
page_title = "Burt's Books | Home",
header_text = "Welcome to Burt's Books!",
books = ['细说php','python','PHP','小时代']
将会通过参数传送到这里来
tornado的模板里可以使用python的代码,加上{% %}当使用if for while等要使用{% end %}结尾
代码中{% module Book(book) %} 将会调用入口服务文件中的定义和'Book'所对应的模块
ui_modules={'Hello':HelloModule,'Book':BookModule} 也就是BookModule,查看上面的BookModule定义
class BookModule(tornado.web.UIModule): def render(self,bookname): doubanapi = r'https://api.douban.com/v2/book/' searchapi = r'https://api.douban.com/v2/book/search?q=' searchurl = searchapi+bookname searchresult = urllib2.urlopen(searchurl).read() bookid = json.loads(searchresult)['books'][0]['id'] bookurl = doubanapi+bookid injson = urllib2.urlopen(bookurl).read() bookinfo = json.loads(injson) return self.render_string('modules/book.html',book = bookinfo)
BookModule 继承自tornado.web.UIModule,UI模块的使用是最后render_string()方法来把一个对象渲染到一个模板中去,我这里简单 的使用了豆瓣的图书api,先通过search来查询一下包含关键词的图书信息,返回第一条图书的id,再使用book api来查询该图书的具体信息,将这个具体图书的信息render到对应的模板
在templates 目录下创建modules目录,再下创建一个book.html,这里是具体的book要显示的内容框架
<div class="book"> <h3 class="book_title">{{ book["title"] }}</h3> <a href="{{book['alt']}}" target="_blank"><p>点击查看详情</p></a> {% if book["subtitle"] != "" %} <h4 class="book_subtitle">{{ book["subtitle"] }}</h4> {% end %} <img src="{{ book["images"]["large"] }}" class="book_image"/> <div class="book_details"> <div class="book_date_released">Released: {{ book["pubdate"]}}</div> <h5>Description:</h5> <div class="book_body">{% raw book["summary"] %}</div> </div> </div>
最后的文件目录结构应该是这样的
├── blockmain.py └── templates ├── index.html ├── main.html └── modules └── book.html
程序的执行是这样的
先通过路径‘/’来使用MainHandler类访问index.html---->index.html继承自 main.html---->index.html中的{% module Book(book) %}反过来查找blockmain.py中的Book对应的ui_modules---->ui_modules中将查询得到的book对象内容渲 染到modules下的book.html中,这样就把完整的内容呈现出来了,没有做前端…… 通过python blockmain.py启动服务,通过http://localhost:8000 来访问得到如下的网页