Tornado 簡單入門教程(一)——Demo1

前面的話

Demo1是一個簡單的博客系統(=。=什麼網站都叫系統)。咱們從這個簡單的系統入手,去了解P+T+M網站的內部邏輯,並記住一些「規則」,方便咱們進一步本身開發。javascript

「規則」這個詞特地打上了雙引號,目的是想借此聲明一點:本教程內不會將各語句背後的原理逐一講明(事實上我也講不清楚哈哈)。個人着重點將在「怎樣快速學會使用這個‘框架’去搭建咱們想要的網站」,即「怎樣快速上手一個工具」。因爲本人在技術上研究不深刻不細緻,因此用詞或者內容上不免有不規範或錯誤之處,能理解的就自行理解哈。固然願意斧正的歡迎指出。css

對了,本教程默認讀者是有web開發基礎的,明白「渲染」、「get請求」、「post請求」等分別是什麼意思。html

講解模式

基本的是:java

  • 列出項目目錄結構
  • 展現源碼,經過部分源碼註釋(紅色字)講解
  • 列表項目
  • 根據網站邏輯結合「代碼回顧」進行講解

但願你們複製源碼(記得把紅字註釋刪除)根據項目目錄結構建立項目,或者直接將附件中的代碼包拷到你的項目目錄,跟着講解一步一步試驗。python

OK,開始。web

Demo1項目目錄結構

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運行了咱們這個網站。
demo11.png
代碼回顧:

define(「port」, default=8002, help=」run on the given port」, type=int)

此時打開瀏覽器,訪問http://127.0.0.1:8002,咱們能夠發現網站已經能夠正常訪問。

再看終端窗口,發現已經接收到了一個get請求。
demo13.png
服務器是怎麼樣根據咱們的請求而後輸出給咱們相應頁面的呢?

Handlers 和 settings

代碼回顧:

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中初始化了兩個東西(數據庫鏈接另說),分別是handlerssettings

顧名思義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類裏定義了getpost兩個方法。

學習tornado讓我很感動的一點就是,它讓我明白了其實咱們在訪問網頁的時候歸根結底就是向網站發出了兩種請求(這個認識可能比較淺薄)。

一種是get請求,即打開某個頁面。
另外一種是post請求,即向某個頁面提交表單(數據)。

tornado中,默認使用getpost函數分別處理兩種請求。因此當咱們訪問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/時,最終看到的就是下圖:
demo12.png
代碼回顧:

<form method=」post」>

此時,咱們看到了首頁的表單。來看看index.html的源碼:表單的提交方式是post;請求的頁面(action)沒有填寫,表示提交到當前頁面。如今將表單填充,點擊發布。
demo14.png
此時查看終端窗口,咱們發現,服務器收到了一個post請求。
demo15.png
由於表單仍提交到當前頁面,因此仍是由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方法分別獲取表單中titlecontent兩個數據,進行簡單的判斷,當兩者均不爲空時,將其存入預約義的blog變量中,而且給blog[‘date’]賦值爲當前的時間戳。
import time載入時間相關的的一個類,time.time()獲取當前時間戳。 coll = self.application.db.blog獲取數據庫中的名爲blogcollection
coll.insert(blog)blog變量插入collection中。
self.redirect(‘/blog’)頁面跳轉至博客頁。在使用redirect函數時,參數爲頁面相對路徑。

簡單來講:post方法接收了表單數據並將其插入對應數據集中,而後頁面跳轉到博客頁。

頁面跳轉,本質就是訪問跳轉後的頁面,即向此頁面發送get請求。咱們看一下終端窗口:
demo16.png
也就是說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的值。

(關於Tornadohtml文件裏面對Python語句的使用方法,我會在另一篇總結中寫出來。連接:《Tornado,在模板裏使用Python語句》

locale.format_date()是一個時間格式化函數。locale.format_date(blog[‘date’], relative=False) }}是將blog[‘date’]的值格式化輸出。
因此最終渲染出的頁面以下圖:
demo17.png


至此,一個最簡單的博客系統就完成了。

相關文章
相關標籤/搜索