在Demo1
裏面,咱們練習了如何部署應用、tornado
框架的基本結構以及應用如何處理請求。
其實Demo1
算不上一個博客啦。一個最基本的信息系統必定要包含對數據庫的增
、刪
、改
和查
。因此此次,咱們來將Demo1
升級爲Demo2
,添加上基本的增刪改查
。javascript
終於=。=在github上建立了項目,把源碼傳上去了。有須要的同窗本身去下載吧。
https://github.com/cAntCheng/simple_tutorial_of_tornadocss
吶,仍是把源碼在這裏貼一下html
demo.pyjava
#!/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 pymongo define("port", default=8002, help="run on the given port", type=int) class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/", MainHandler), (r"/edit/([0-9Xx\-]+)", EditHandler), (r"/add", EditHandler), (r"/delete/([0-9Xx\-]+)", DelHandler), (r"/blog/([0-9Xx\-]+)", 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", 27017) self.db = conn["demo2"] tornado.web.Application.__init__(self, handlers, **settings) class MainHandler(tornado.web.RequestHandler): def get(self): import time coll = self.application.db.blog blogs = coll.find().sort("id",pymongo.DESCENDING) self.render( "index.html", blogs = blogs, time = time, ) class EditHandler(tornado.web.RequestHandler): def get(self, id=None): blog = dict() if id: coll = self.application.db.blog blog = coll.find_one({"id": int(id)}) self.render("edit.html", blog = blog) def post(self, id=None): import time coll = self.application.db.blog blog = dict() if id: blog = coll.find_one({"id": int(id)}) blog['title'] = self.get_argument("title", None) blog['content'] = self.get_argument("content", None) if id: coll.save(blog) else: last = coll.find().sort("id",pymongo.DESCENDING).limit(1) lastone = dict() for item in last: lastone = item blog['id'] = int(lastone['id']) + 1 blog['date'] = int(time.time()) coll.insert(blog) self.redirect("/") class DelHandler(tornado.web.RequestHandler): def get(self, id=None): coll = self.application.db.blog if id: blog = coll.remove({"id": int(id)}) self.redirect("/") class BlogHandler(tornado.web.RequestHandler): def get(self, id=None): import time coll = self.application.db.blog if id: blog = coll.find_one({"id": int(id)}) self.render("blog.html", page_title = "個人博客", blog = blog, time = time, ) 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.htmlpython
{% autoescape None %} <!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") }}"> </head> <body> <div class="main"> <a href="/"> <img class="logo" src="{{ static_url("img/logo.png") }}"> </a> <div class="container"> <h1>歡迎訪問B06創新實驗室的博客</h1> {% autoescape None %} {% for blog in blogs %} <div class="content"> <div class="Title"> <p> <a href="/blog/{{ int(blog['id']) }}">{{ blog['title'] }}</a> </p> <p> <span class="Time">{{ time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(blog['date']) ) }}</span> </p> </div> <div class="Article"> <p> {{ blog['content'] }} </p> </div> </div> {% end %} <a href="/add"> <input type="button" class="Article Button Submit" value="發 布" onclick="document.location.href('/add/')"/> </a> </div> </div> </body> </html>
blog.htmlmysql
{% autoescape None %} <!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"> <a href="/"> <img class="logo" src="{{ static_url("img/logo.png") }}"> </a> <div class="container"> <h1>歡迎訪問B06創新實驗室的博客</h1> <div class="content"> <div class="Title"> <p> {{ blog['title'] }} </p> <p> <span class="Time">{{ time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(blog['date']) ) }}</span> </p> </div> <div class="Article"> <p> {{ blog['content'] }} </p> </div> <div class="buttonDiv"> <a href="/edit/{{ int(blog['id']) }}"> <input type="button" class="Article Button Edit" value="編 輯"/> </a> <a href="/delete/{{ int(blog['id']) }}"> <input type="button" class="Article Button Delete" value="刪 除"/> </a> </div> </div> </div> </div> </body> </html>
edit.htmlgit
<!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"> <a href="/"> <img class="logo" src="{{ static_url("img/logo.png") }}"> </a> <div class="container"> <h1>歡迎訪問B06創新實驗室的博客</h1> <div class="content"> {% if blog.get('id', None) %} <form method="post" action="/edit/{{ int(blog['id']) }}"> {% else%} <form method="post" action="/add"> {% end %} <p>文章標題:</p> <input type="text" class="Title" name="title" placeholder="在這裏輸入你的標題" value="{{ blog.get('title', '') }}" /> <p>文章正文:</p> <textarea type="text" class="Article" id="myArticle" name="content" placeholder="在這裏輸入你的正文"> {{ blog.get('content', '') }} </textarea> <br/> {% if blog.get('id', None) %} <input type="submit" class="Article Button Submit" value="修 改"/> {% else%} <input type="submit" class="Article Button Submit" value="發 布"/> {% end %} </form> </div> </div> </div> </body> </html>
代碼回顧:github
handlers = [ (r"/", MainHandler), (r"/edit/([0-9Xx\-]+)", EditHandler), (r"/add", EditHandler), (r"/delete/([0-9Xx\-]+)", DelHandler), (r"/blog/([0-9Xx\-]+)", BlogHandler), ]
在Demo2
中定義了5個handler
,分別是web
(r"/", MainHandler)
->博客列表 (r"/edit/([0-9Xx\-]+)", MainHandler)
->編輯博客 (r"/add", MainHandler)
->發表博客 (r"/delete/([0-9Xx\-]+)", MainHandler)
->刪除博客 (r"/blog/([0-9Xx\-]+)", MainHandler)
->查看博客 經過這五個handler
,咱們終於能寫出一個真正的博客了噢耶。正則表達式
有沒有注意到/([0-9Xx\-]+)
!!!這是幹嗎的呢?恩,這是一個url
參數。這個正則表達式規定參數由0-9
的數字和X
組成。
在edit
、delete
和blog
方法中,咱們須要一個博客id參數來找到指定的博客進行響應操做,因此咱們在這裏添加了一個url
參數。
代碼回顧:
class MainHandler(tornado.web.RequestHandler): def get(self): import time coll = self.application.db.blog blogs = coll.find().sort("id",pymongo.DESCENDING) self.render( "index.html", blogs = blogs, time = time, )
在MainHandler
中,咱們經過find()
查詢全部的博客,並經過sort("id",pymongo.DESCENDING)
對博客id
進行倒序排序
(由於id
越大,博文就越新,那它就應該在越前面顯示嘛)。
同時載入了time
模塊,方便咱們在模板裏輸出正確格式的時間。
代碼回顧:
<span class="Time">{{ time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(blog['date']) ) }}</span>
看看index.html
,在輸出時間的這一行,調用了time
模塊的strftime()
方法,將在數據庫儲存的時間按照制定的格式%Y-%m-%d %H:%M:%S
輸出。
注意:能夠看到,在
self.render()
方法中,咱們將time
對象做爲參數傳遞到模板中。
這樣咱們才能夠在模板中調用time
對象的方法。
代碼回顧:
{% for blog in blogs %} ... {% end %}
經過for
循環輸出博客列表。
來看看效果(不要在乎裏面亂七八糟的內容=。=):
代碼回顧:
(r"/add", EditHandler),
有沒有以爲奇怪=。=,爲何add
(發表博客
)調用的仍是EditHandler
。
解釋:
通常的博客系統,發表博客
和編輯博客
的模板
(html文件
)實際上是同樣的。無非是編輯博客
的模板
裏填充了博客的內容,而發表博客
的模板
是一個空表單
。
所以咱們經常把新增
和編輯
放在同一個方法裏處理。具體的處理方法咱們下面繼續聊。
代碼回顧:
class EditHandler(tornado.web.RequestHandler): def get(self, id=None): blog = dict() if id: coll = self.application.db.blog blog = coll.find_one({"id": int(id)}) self.render("edit.html", blog = blog)
在get
方法中,咱們新建了一個空字典blog
。接下來判斷id
是否爲空。咱們經過判斷id
是否爲空來選擇下一步是編輯
仍是新增
。
代碼回顧:
<a href="/add"> <input type="button" class="Article Button Submit" value="發 布"/> </a>
思考一下業務邏輯,當咱們點擊首頁發佈按鈕,即訪問http://127.0.0.1:8002/add
,由EditHandler
執行get
方法。
代碼回顧:
def get(self, id=None):
由於沒有傳遞url
參數,因此id
賦值爲默認值None
。
代碼回顧:
if id: coll = self.application.db.blog blog = coll.find_one({"id": int(id)}) self.render("edit.html", blog = blog)
由於id = None
,因此if id:
爲false
。所以接下來執行self.render("edit.html",blog = blog)
。
代碼回顧:
{% if blog.get('id', None) %} <form method="post" action="/edit/{{ int(blog['id']) }}"> {% else%} <form method="post" action="/add"> {% end %} <p>文章標題:</p> <input type="text" class="Title" name="title" placeholder="在這裏輸入你的標題" value="{{ blog.get('title', '') }}" /> <p>文章正文:</p> <textarea type="text" class="Article" id="myArticle" name="content" placeholder="在這裏輸入你的正文"> {{ blog.get('content', '') }} </textarea> <br/> {% if blog.get('id', None) %} <input type="submit" class="Article Button Submit" value="修 改"/> {% else%} <input type="submit" class="Article Button Submit" value="發 布"/> {% end %} </form>
在edit.html
中,咱們經過判斷blog.get('id', None)
的值是否爲None
來輸出不一樣的form
標籤。
dict.get('key','Defalt')
這個方法用於獲取字典
中指定鍵名
的鍵值
(第一個參數),若是該鍵名
不存在,則返回第二個參數設定的默認值
。
要調用這個方法,咱們必須有一個字典
能夠查找。因此在EditHandler
的get
方法中,咱們定義了一個空字典
:blog
(還記得嗎?回頭看一下代碼吧。)
繼續剛纔的業務邏輯,咱們渲染了edit.html
,並將空字典
:blog
做爲參數傳遞到模板
文件中。因此blog.get('id', None) == None
,因此輸出<form method="post" action="/add">
。
下面的{{ blog.get('title', '') }}
和{{ blog.get('content', '') }}
,同上,均爲空字符串。因此咱們最終獲得了一個空表單
頁面,也就是咱們的發佈博客
頁面。
當咱們填寫好表單,點擊發佈按鈕
,表單就以POST
方式被提交到/add
(相對路徑
,對應的絕對路徑
爲http://127.0.0.1:8002/add
)。這時候EditHandler
執行post
方法。
代碼回顧:
def post(self, id=None): import time coll = self.application.db.blog blog = dict() if id: blog = coll.find_one({"id": int(id)}) blog['title'] = self.get_argument("title", None) blog['content'] = self.get_argument("content", None) if id: coll.save(blog) else: last = coll.find().sort("id",pymongo.DESCENDING).limit(1) lastone = dict() for item in last: lastone = item blog['id'] = int(lastone['id']) + 1 blog['date'] = int(time.time()) coll.insert(blog) self.redirect("/")
在post
方法中,一樣的邏輯判斷執行新增
仍是編輯
。
代碼回顧:
blog['title'] = self.get_argument("title", None) blog['content'] = self.get_argument("content", None)
將獲取的表單填充到空字典
:blog
中,而後調用coll.insert(blog)
方法向數據庫中插入文檔
。
誒誒誒,等等,好像插入以前還有好幾行啊!!
好吧=。=這幾行代碼是用來生成新博客id
的。咱們的博客須要一個id
來標識身份
,因此咱們給每一篇博客設置一個id
。通常就是用一個從1
開始自增
的整數做爲id
啦。
解釋:
由於mongodb
不像mysql
那樣能夠設置自增字段
,因此咱們須要本身生成自增
的id
(查過資料應該是有辦法設置自增,可是文檔沒太看明白=。=就當沒有辦法吧。除了文檔中的辦法,我查了很久,發現都是靠本身寫函數生成自增
的id
,你們有興趣能夠本身去查一下。在這裏我就用本身的方法了。)
個人方法是這樣的:last = coll.find().sort("id",pymongo.DESCENDING).limit(1)
倒序查詢數據庫獲取最後id
最大的那一條記錄。由於find()
函數返回的結果是一個數組?
因此要用for
循環取值(由於只查詢了一條記錄,因此用一個循環也不會太奢侈吧啦啦啦)。blog['id'] = int(lastone['id']) + 1
將最大id
加1
賦值給新博客的id
。由於mongodb
在存整型
數的時候好像會默認存爲浮點型
(具體能夠自行百度),因此用int()
方法處理lastone['id']
保證咱們處理過程當中數據類型的正確。
好了,這樣咱們終於成功新增
了一篇博客!!!
當咱們在首頁點擊某一篇博客的標題的時候,好比點擊第二篇,咱們會訪問http://127.0.0.1:8002/blog/2
,進入到這個頁面:
這個過程是這樣的:/blog
對應BlogHandler
,執行get
方法。
代碼回顧:
class BlogHandler(tornado.web.RequestHandler): def get(self, id=None): import time coll = self.application.db.blog if id: blog = coll.find_one({"id": int(id)}) self.render("blog.html", page_title = "個人博客", blog = blog, time = time, ) else: self.redirect("/")
if
判斷id
是否存在,不存在則跳轉到首頁
。咱們這裏get
方法獲取到url
參數,所以id=2
。blog = coll.find_one({"id": int(id)})
查詢該博客並渲染
博客頁面。
這樣,咱們就完成了博客查看的功能。
留一個小問題。
由於這個demo
是剛開始學tornado
的時候寫的,因此代碼其實寫得很糟糕。BlogHandler
可能這樣寫會更好一點,初學的同窗看一看,思考一下爲何。我就不改demo
裏的代碼了。class BlogHandler(tornado.web.RequestHandler): def get(self, id=0): import time coll = self.application.db.blog blog = coll.find_one({"id": int(id)}) if blog: self.render("blog.html", page_title = "個人博客", blog = blog, time = time, ) else: self.redirect("/")
代碼回顧:
class EditHandler(tornado.web.RequestHandler): def get(self, id=None): blog = dict() if id: coll = self.application.db.blog blog = coll.find_one({"id": int(id)}) self.render("edit.html", blog = blog) def post(self, id=None): import time coll = self.application.db.blog blog = dict() if id: blog = coll.find_one({"id": int(id)}) blog['title'] = self.get_argument("title", None) blog['content'] = self.get_argument("content", None) if id: coll.save(blog) else: last = coll.find().sort("id",pymongo.DESCENDING).limit(1) lastone = dict() for item in last: lastone = item blog['id'] = int(lastone['id']) + 1 blog['date'] = int(time.time()) coll.insert(blog) self.redirect("/")
本身看,就很少說了=。=
刪除操做的邏輯是,傳遞博客id
給DelHandler
,而後調用remove()
方法從數據庫刪除指定博客。
代碼回顧:
class DelHandler(tornado.web.RequestHandler): def get(self, id=None): coll = self.application.db.blog if id: blog = coll.remove({"id": int(id)}) self.redirect("/")
<( ̄ˇ ̄)/相信聰明的你已經看懂了!
至此,咱們練習了mongodb
的增刪改查
,也實現了應用
的增刪改查
。
固然這個Demo
還有不少須要改進的地方,好比:
http://127.0.0.1:8002/delete?id=2
的連接,經過get
方法傳遞參數。格式驗證
。同窗們能夠本身稍做修改嘗試一下。