Demo1是一个简单的博客系统(=。=什么网站都叫系统)。咱们从这个简单的系统入手,去了解P+T+M网站的内部逻辑,并记住一些“规则”,方便咱们进一步本身开发。javascript
“规则”这个词特地打上了双引号,目的是想借此声明一点:本教程内不会将各语句背后的原理逐一讲明(事实上我也讲不清楚哈哈)。个人着重点将在“怎样快速学会使用这个‘框架’去搭建咱们想要的网站”,即“怎样快速上手一个工具”。因为本人在技术上研究不深刻不细致,因此用词或者内容上不免有不规范或错误之处,能理解的就自行理解哈。固然愿意斧正的欢迎指出。css
对了,本教程默认读者是有web开发基础的,明白“渲染”、“get请求”、“post请求”等分别是什么意思。html
基本的是:java
但愿你们复制源码(记得把红字注释删除)根据项目目录结构建立项目,或者直接将附件中的代码包拷到你的项目目录,跟着讲解一步一步试验。python
OK,开始。web
demo1 demo.py -static -css style.css -img bg.jpg logo.png -templates index.html blog.html
demo.py数据库
#!/usr/bin/env python # -*- coding: utf-8 -*- #用于规定字符编码,想要中文正常最好就加上这句 import os.path import tornado.auth import tornado.escape import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options #巴拉巴拉import一大堆tornado的东西,反正都有用,原封不动便可 import pymongo #这里是导入MongoDB define(“port”, default=8002, help=”run on the given port”, type=int) #定义监听的端口,随便挑个喜欢的数字吧 class Application(tornado.web.Application): def __init__(self): handlers = [ (r”/”, MainHandler), (r”/blog”, BlogHandler), ] settings = dict( template_path=os.path.join(os.path.dirname(__file__), “templates”), static_path=os.path.join(os.path.dirname(__file__), “static”), debug=True, ) conn = pymongo.Connection(“localhost”, 12345) self.db = conn[“demo”] tornado.web.Application.__init__(self, handlers, **settings) class MainHandler(tornado.web.RequestHandler): def get(self): self.render(“index.html”,) def post(self): import time title = self.get_argument(‘title’, None) content = self.get_argument(‘content’, None) blog = dict() if title and content: blog[‘title’] = title blog[‘content’] = content blog[‘date’] = int(time.time()) coll = self.application.db.blog coll.insert(blog) self.redirect(‘/blog’) self.redirect(‘/’) class BlogHandler(tornado.web.RequestHandler): def get(self): coll = self.application.db.blog blog = coll.find_one() if blog: self.render(“blog.html”, page_title = blog[‘title’], blog = blog, ) else: self.redirect(‘/’) def main(): tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start() if __name__ == “__main__”: main()
index.html浏览器
<!DOCTYPE html> <html lang=”zh-CN”> <head> <meta charset=”utf-8″> <title>B06 Innovation Space</title> <link rel=”stylesheet” type=”text/css” href=”{{ static_url(“css/style.css”) }}”> <script type=”text/javascript”> window.setInterval(function() { go_to(); }, 100); function go_to() { if( document.getElementById(“myArticle”).style.height < (document.getElementById(“myArticle”).scrollHeight – 4 ) + “px”) document.getElementById(“myArticle”).style.height = document.getElementById(“myArticle”).scrollHeight + “px”; } </script> </head> <body> <div class=”main”> <img class=”logo” src=”{{ static_url(“img/logo.png”) }}”> <div class=”container”> <h1>欢迎访问B06创新实验室的博客</h1> <div class=”content”> <form method=”post”> <p>文章标题:</p> <input type=”text” class=”Title” name=”title” placeholder=”在这里输入你的标题” /> <p>文章正文:</p> <textarea type=”text” class=”Article” id=”myArticle” name=”content” placeholder=”在这里输入你的正文”></textarea> <br/> <input type=”submit” class=”Article Button Submit” value=”发 布”/> </form> </div> </div> </div> </body> </html>
blog.html服务器
<!DOCTYPE html> <html lang=”zh-CN”> <head> <meta charset=”utf-8″> <title>{{ page_title }}</title> <link rel=”stylesheet” type=”text/css” href=”{{ static_url(“css/style.css”) }}”> </head> <body> <div class=”main”> <img class=”logo” src=”{{ static_url(“img/logo.png”) }}”> <div class=”container”> <h1>欢迎访问B06创新实验室的博客</h1> <div class=”content”> <div class=”Title”> <p> {{ blog[‘title’] }} <span class=”Time”>{{ locale.format_date(blog[‘date’], relative=False) }}</span> </p> </div> <div class=”Article”> <p>{{ blog[‘content’] }}</p> </div> </div> </div> </div> </body> </html>
从头开始说。
部署用Python开发的网站,须要在服务器上运行一个主文件,好比demo1的部署:网络
打开终端,cd到项目文件夹,执行python demo.py
命令,此时python就在设定好的默认端口8002运行了咱们这个网站。
代码回顾:
define(“port”, default=8002, help=”run on the given port”, type=int)
此时打开浏览器,访问http://127.0.0.1:8002
,咱们能够发现网站已经能够正常访问。
再看终端窗口,发现已经接收到了一个get请求。
服务器是怎么样根据咱们的请求而后输出给咱们相应页面的呢?
代码回顾:
class Application(tornado.web.Application): def __init__(self): handlers = [ (r”/”, MainHandler), (r”/blog”, BlogHandler), ] settings = dict( template_path=os.path.join(os.path.dirname(__file__), “templates”), static_path=os.path.join(os.path.dirname(__file__), “static”), debug=True, ) conn = pymongo.Connection(“localhost”, 12345) self.db = conn[“demo”] tornado.web.Application.__init__(self, handlers, **settings)
咱们看到在Application
中初始化了两个东西(数据库链接另说),分别是handlers
和settings
。
顾名思义settings
就是项目的各类设置,其中template_path
用于指定咱们以后要渲染的html文件的文件夹位置,而static_path
用于指定以后要用的的一些引用文件(如css文件、js文件、图片等)的文件夹位置 (我偏不解释debug是干什么的:-) )。handler
,我百度翻译了一下,是“处理者”的意思。它的做用就是处理咱们向服务器提交的请求。怎么处理呢?
以(r”/”, MainHandler)
为例,双引号中间是要访问页面的相对路径,然后面的XxxHandler
表示这个路径对应的“处理者类”。初始化如上handlers
后,当咱们访问http://127.0.0.1:8002/
时,咱们的请求会被交给MainHandler
处理;一样的,当咱们访问http://127.0.0.1:8002/blog
时,咱们的请求将会被交给BlogHandler
处理。也就是说handlers用于设定url与处理类间的映射关系。
在以后的几个demo里面,经过对更多handler
的设置,咱们会慢慢对handler
了解得更清楚一些,不要着急。反正大概意思就是一个网址对应一个handler
呗。
代码回顾:
class Application(tornado.web.Application): …… conn = pymongo.Connection(“localhost”, 12345) self.db = conn[“demo”] tornado.web.Application.__init__(self, handlers, **settings)
而后说一下这个部分。聪明的人一看就知道这是数据库链接嘛。
第一句:参数一,数据库服务器地址,咱们如今是本地服务器因此用localhost
;参数二,端口号,端口号固然是你本地MongoDB使用的端口号。这样就链接上MongoDB了。
第二句:self.db = conn[“demo”]
,选择一个数据库,把数据库名字放在双引号中间。个人数据库就叫作demo
。最后一句就是把以前的设置都初始化嘛,init就是初始化,聪明人都懂的。
OK,回过头来,刚才说到当用户访问一个页面的时候,根据咱们初始化的handler
,服务器会将不一样的请求分发给不一样的处理类进行处理。
咱们的Demo1包含两个页面。首页有一个表单,提交表单后跳转至博客页面。咱们先来看看访问首页时是怎么处理的。
当咱们访问http://127.0.0.1:8002/
时,咱们的请求会被交给MainHandler
处理,即执行这个类里对应的内容。
代码回顾:
class MainHandler(tornado.web.RequestHandler):
处理类的定义规则是class XxxxHandler(tornado.web.RequestHandler)
。Request
请求,Handler
处理者,处理请求的一个类嘛。括号里面的东西确定跟tornado、跟网络什么的有关,乖乖地复制就行了。
代码回顾:
def get(self):#用于处理get请求,默认参数是self self.render(“index.html”,) def post(self):#用于处理post请求,默认参数是self import time title = self.get_argument(‘title’, None) content = self.get_argument(‘content’, None) blog = dict() if title or content: blog[‘title’] = title blog[‘content’] = content blog[‘date’] = int(time.time()) coll = self.application.db.blog coll.insert(blog) self.redirect(‘/blog/’) self.redirect(‘/’)
咱们看到MainHandler
类里定义了get
和post
两个方法。
学习tornado
让我很感动的一点就是,它让我明白了其实咱们在访问网页的时候归根结底就是向网站发出了两种请求(这个认识可能比较浅薄)。
一种是
get
请求,即打开某个页面。
另外一种是post
请求,即向某个页面提交表单(数据)。
在tornado
中,默认使用get
和post
函数分别处理两种请求。因此当咱们访问http://127.0.0.1:8002/
时,服务器就会执行MainHandler
中的get
函数。
代码回顾:
def get(self): self.render(“index.html”,)
render
就是渲染的意思。这一句就是:在浏览器中渲染出index.html
这个文件的内容。index.html
文件在哪里呢?从项目目录结构中能够看到,它在templates
文件夹中。还记得settings
中定义的templates_path
吗?咱们在render()
中的双引号内填入templates
文件夹中文件的相对路径便可,服务器会根据templates_path
自动补全路径,找到文件,并将之渲染出来。
插一句:在用
tornado
开发时,咱们经常使用的目录结构正如demo1
的目录结构。根目录下放置python
文件,templates
文件夹中放置html文件,static
文件夹中放置引用文件。
因此,当咱们访问http://127.0.0.1:8002/
时,最终看到的就是下图:
代码回顾:
<form method=”post”>
此时,咱们看到了首页的表单。来看看index.html
的源码:表单的提交方式是post
;请求的页面(action
)没有填写,表示提交到当前页面。如今将表单填充,点击发布。
此时查看终端窗口,咱们发现,服务器收到了一个post请求。
由于表单仍提交到当前页面,因此仍是由MainHandler
处理。而此时的请求类型为post
,因此服务器将执行MainHandler
中的post
方法。
代码回顾:
def post(self): import time title = self.get_argument(‘title’, None) content = self.get_argument(‘content’, None) blog = dict() if title and content: blog[‘title’] = title blog[‘content’] = content blog[‘date’] = int(time.time()) coll = self.application.db.blog coll.insert(blog) self.redirect(‘/blog’)
tornado
中经过self.get_argument()
获取表单数据,其中第一个参数为数据名称,第二个参数为当没有获取到该表单数据时的替代值。post
方法分别获取表单中title
和content
两个数据,进行简单的判断,当两者均不为空时,将其存入预约义的blog
变量中,而且给blog[‘date’]
赋值为当前的时间戳。import time
载入时间相关的的一个类,time.time()
获取当前时间戳。coll = self.application.db.blog
获取数据库中的名为blog
的collection
。coll.insert(blog)
将blog
变量插入collection
中。self.redirect(‘/blog’)
页面跳转至博客页。在使用redirect
函数时,参数为页面相对路径。
简单来讲:post
方法接收了表单数据并将其插入对应数据集中,而后页面跳转到博客页。
页面跳转,本质就是访问跳转后的页面,即向此页面发送get
请求。咱们看一下终端窗口:
也就是说self.redirect(‘/blog’)
这一句,就是访问http://127.0.0.1:8002/blog
,服务器获得get
请求,而后让BlogHandler
对其进行处理,执行get
方法。
代码回顾:
def get(self): coll = self.application.db.blog blog = coll.find_one() if blog: self.render(“blog.html”, page_title = blog[‘title’], blog = blog, ) else: self.redirect(‘/’)
那咱们看看函数内容。
coll = self.application.db.blog
依旧是获取名字为blog
的数据集blog = coll.find_one()
获取一条数据。由于咱们是一个最简版的demo
,因此就获取一条数据。if
判断数据是否存在,存在则渲染博客页面,不存在则跳转至首页。render
函数第一个参数是要渲染的html
文件名,后面的参数为传递到页面的数据,参数间用逗号隔开。page_title = blog[‘title’]
我选择用博客标题做为title
,因此传了一个page_title
过去。blog = blog
把博客内容传递过去。
什么叫把数据传递到html
文件去呢?就是把咱们动态获取的数据库数据和html
文件一块儿渲染,把数据在html
代码中输出来。
For一个sample:-):
代码回顾:
<div class=”Title”> <p> {{ blog[‘title’] }} <span class=”Time”>{{ locale.format_date(blog[‘date’], relative=False) }}</span> </p> </div> <div class=”Article”> <p>{{ blog[‘content’] }}</p> </div>
在html文件中能够经过特殊语法输出咱们传递过来的数据。
{{ 变量名 }}
,双大括号中加上变量名,就是输出该变量。好比:{{ blog[‘title’] }}
就是输出blog
中的title
值;{{blog[‘content’] }}
就是输出blog
中的content
值;还有{{ page_title }}
就是输出page_title
的值。(关于
Tornado
在html
文件里面对Python
语句的使用方法,我会在另一篇总结中写出来。连接:《Tornado,在模板里使用Python语句》)
locale.format_date()
是一个时间格式化函数。locale.format_date(blog[‘date’], relative=False) }}
是将blog[‘date’]
的值格式化输出。
因此最终渲染出的页面以下图:
至此,一个最简单的博客系统就完成了。