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’]
的值格式化輸出。
因此最終渲染出的頁面以下圖:
至此,一個最簡單的博客系統就完成了。