廖雪峯《python3 基礎教程》讀書筆記——第二十一章 Web開發

第二十一章 Web開發



1、CS架構:

軟件開始主要運行在桌面上,而數據庫這樣的軟件運行在服務器端,這種Client/Server模式簡稱CS架構。

CS架構不適合Web,最大的原因是Web應用程序的修改和升級非常迅速,而CS架構需要每個客戶端逐個升級桌面App。因此,Browser/Server模式開始流行,簡稱BS架構。

2、BS架構

BS架構下,客戶端只需要瀏覽器,應用程序的邏輯和數據都存儲在服務器端。瀏覽器只需要請求服務器,獲取Web頁面,並把Web頁面展示給用戶即可。

當然,Web頁面也具有極強的交互性。由於Web頁面是用HTML編寫的,而HTML具備超強的表現力,並且,服務器端升級後,客戶端無需任何部署就可以使用到新的版本,因此,BS架構迅速流行起來。

今天,除了重量級的軟件如Office,Photoshop等,大部分軟件都以Web形式提供。比如,新浪提供的新聞、博客、微博等服務,均是Web應用。

3、Web應用開發可以說是目前軟件開發中最重要的部分。Web開發也經歷了好幾個階段:

靜態Web頁面:由文本編輯器直接編輯並生成靜態的HTML頁面,如果要修改Web頁面的內容,就需要再次編輯HTML源文件,早期的互聯網Web頁面就是靜態的;

CGI:由於靜態Web頁面無法與用戶交互,比如用戶填寫了一個註冊表單,靜態Web頁面就無法處理。要處理用戶發送的動態數據,出現了Common Gateway Interface,簡稱CGI,用C/C++編寫。

ASP/JSP/PHP:由於Web應用特點是修改頻繁,用C/C++這樣的低級語言非常不適合Web開發,而腳本語言由於開發效率高,與HTML結合緊密,因此,迅速取代了CGI模式。ASP是微軟推出的用VBScript腳本編程的Web開發技術,而JSP用Java來編寫腳本,PHP本身則是開源的腳本語言。

MVC:爲了解決直接用腳本語言嵌入HTML導致的可維護性差的問題,Web應用也引入了Model-View-Controller的模式,來簡化Web開發。ASP發展爲ASP.Net,JSP和PHP也有一大堆MVC框架。

目前,Web開發技術仍在快速發展中,異步開發、新的MVVM前端技術層出不窮。

Python的誕生歷史比Web還要早,由於Python是一種解釋型的腳本語言,開發效率高,所以非常適合用來做Web開發。

Python有上百種Web開發框架,有很多成熟的模板技術,選擇Python開發Web應用,不但開發效率高,而且運行速度快。

21.1 HTTP協議簡介

Web應用中,服務器把網頁傳給瀏覽器,實際上就是把網頁的HTML代碼發送給瀏覽器,讓瀏覽器顯示出來。而瀏覽器和服務器之間的傳輸協議是HTTP,所以:

 

1)HTML是一種用來定義網頁的文本,會HTML,就可以編寫網頁;

2)HTTP是在網絡上傳輸HTML的協議,用於瀏覽器和服務器的通信。

在舉例子之前,我們需要安裝Google的Chrome瀏覽器。

爲什麼要使用Chrome瀏覽器而不是IE呢?因爲IE實在是太慢了,並且,IE對於開發和調試Web應用程序完全是一點用也沒有。

我們需要在瀏覽器很方便地調試我們的Web應用,而Chrome提供了一套完整地調試工具,非常適合Web開發。

安裝好Chrome瀏覽器後,打開Chrome,在菜單中選擇“視圖”,“開發者”,“開發者工具”,就可以顯示開發者工具:

Elements顯示網頁的結構,Network顯示瀏覽器和服務器的通信。我們點Network,確保第一個小紅燈亮着,Chrome就會記錄所有瀏覽器和服務器之間的通信:

當我們在地址欄輸入www.sina.com.cn時,瀏覽器將顯示新浪的首頁。在這個過程中,瀏覽器都幹了哪些事情呢?通過Network的記錄,我們就可以知道。在Network中,定位到第一條記錄,點擊,右側將顯示Request Headers,點擊右側的view source,我們就可以看到瀏覽器發給新浪服務器的請求:

最主要的頭兩行分析如下,第一行:

GET / HTTP/1.1

GET表示一個讀取請求,將從服務器獲得網頁數據,/表示URL的路徑,URL總是以/開頭,/就表示首頁,最後的HTTP/1.1指示採用的HTTP協議版本是1.1。目前HTTP協議的版本就是1.1,但是大部分服務器也支持1.0版本,主要區別在於1.1版本允許多個HTTP請求複用一個TCP連接,以加快傳輸速度。

從第二行開始,每一行都類似於Xxx: abcdefg:

Host: www.sina.com.cn

表示請求的域名是www.sina.com.cn。如果一臺服務器有多個網站,服務器就需要通過Host來區分瀏覽器請求的是哪個網站。

繼續往下找到Response Headers,點擊view source,顯示服務器返回的原始響應數據:

HTTP響應分爲Header和Body兩部分(Body是可選項),我們在Network中看到的Header最重要的幾行如下:

200 OK

200表示一個成功的響應,後面的OK是說明。失敗的響應有404 Not Found:網頁不存在,500 Internal Server Error:服務器內部出錯,等等。

Content-Type: text/html

Content-Type指示響應的內容,這裏是text/html表示HTML網頁。請注意,瀏覽器就是依靠Content-Type來判斷響應的內容是網頁還是圖片,是視頻還是音樂。瀏覽器並不靠URL來判斷響應的內容,所以,即使URL是http://example.com/abc.jpg,它也不一定就是圖片。

 

HTTP響應的Body就是HTML源碼,我們在菜單欄選擇“視圖”,“開發者”,“查看網頁源碼”就可以在瀏覽器中直接查看HTML源碼:

當瀏覽器讀取到新浪首頁的HTML源碼後,它會解析HTML,顯示頁面,然後,根據HTML裏面的各種鏈接,再發送HTTP請求給新浪服務器,拿到相應的圖片、視頻、Flash、JavaScript腳本、CSS等各種資源,最終顯示出一個完整的頁面。所以我們在Network下面能看到很多額外的HTTP請求。

HTTP請求

跟蹤了新浪的首頁,我們來總結一下HTTP請求的流程:

步驟1:瀏覽器首先向服務器發送HTTP請求,請求包括:

方法:GET還是POST,GET僅請求資源,POST會附帶用戶數據;

路徑:/full/url/path;

域名:由Host頭指定:Host: www.sina.com.cn

以及其他相關的Header;

如果是POST,那麼請求還包括一個Body,包含用戶數據。

步驟2:服務器向瀏覽器返回HTTP響應,響應包括:

響應代碼:200表示成功,3xx表示重定向,4xx表示客戶端發送的請求有錯誤,5xx表示服務器端處理時發生了錯誤;

響應類型:由Content-Type指定;

以及其他相關的Header;

通常服務器的HTTP響應會攜帶內容,也就是有一個Body,包含響應的內容,網頁的HTML源碼就在Body中。

步驟3:如果瀏覽器還需要繼續向服務器請求其他資源,比如圖片,就再次發出HTTP請求,重複步驟1、2。

Web採用的HTTP協議採用了非常簡單的請求-響應模式,從而大大簡化了開發。當我們編寫一個頁面時,我們只需要在HTTP請求中把HTML發送出去,不需要考慮如何附帶圖片、視頻等,瀏覽器如果需要請求圖片和視頻,它會發送另一個HTTP請求,因此,一個HTTP請求只處理一個資源。

HTTP協議同時具備極強的擴展性,雖然瀏覽器請求的是http://www.sina.com.cn/的首頁,但是新浪在HTML中可以鏈入其他服務器的資源,比如<img src="http://i1.sinaimg.cn/home/2013/1008/U8455P30DT20131008135420.png">,從而將請求壓力分散到各個服務器上,並且,一個站點可以鏈接到其他站點,無數個站點互相鏈接起來,就形成了World Wide Web,簡稱WWW。

HTTP格式

每個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。

HTTP協議是一種文本協議,所以,它的格式也非常簡單。

HTTP GET請求的格式:

GET /path HTTP/1.1

Header1: Value1

Header2: Value2

Header3: Value3

每個Header一行一個,換行符是\r\n。

HTTP POST請求的格式:

POST /path HTTP/1.1

Header1: Value1

Header2: Value2

Header3: Value3

body data goes here...

當遇到連續兩個\r\n時,Header部分結束,後面的數據全部是Body。

HTTP響應的格式:

200 OK

Header1: Value1

Header2: Value2

Header3: Value3

 

body data goes here...

HTTP響應如果包含body,也是通過\r\n\r\n來分隔的。請再次注意,Body的數據類型由Content-Type頭來確定,如果是網頁,Body就是文本,如果是圖片,Body就是圖片的二進制數據。

當存在Content-Encoding時,Body數據是被壓縮的,最常見的壓縮方式是gzip,所以,看到Content-Encoding: gzip時,需要將Body數據先解壓縮,才能得到真正的數據。壓縮的目的在於減少Body的大小,加快網絡傳輸。

要詳細瞭解HTTP協議,推薦“HTTP: The Definitive Guide”一書,非常不錯,有中文譯本:

HTTP權威指南

 

21.2 HTML簡介

1、HTML簡介

網頁就是HTML?這麼理解大概沒錯。因爲網頁中不但包含文字,還有圖片、視頻、Flash小遊戲,有複雜的排版、動畫效果,所以,HTML定義了一套語法規則,來告訴瀏覽器如何把一個豐富多彩的頁面顯示出來。

HTML長什麼樣?上次我們看了新浪首頁的HTML源碼,如果仔細數數,竟然有6000多行!

所以,學HTML,就不要指望從新浪入手了。我們來看看最簡單的HTML長什麼樣:

<html>

<head>

  <title>Hello</title>

</head>

<body>

  <h1>Hello, world!</h1>

</body>

</html>

可以用文本編輯器編寫HTML,然後保存爲hello.html,雙擊或者把文件拖到瀏覽器中,就可以看到效果:

hello.html

HTML文檔就是一系列的Tag組成,最外層的Tag是<html>。規範的HTML也包含<head>...</head>和<body>...</body>(注意不要和HTTP的Header、Body搞混了),由於HTML是富文檔模型,所以,還有一系列的Tag用來表示鏈接、圖片、表格、表單等等。

2、CSS簡介

CSS是Cascading Style Sheets(層疊樣式表)的簡稱,CSS用來控制HTML裏的所有元素如何展現,比如,給標題元素<h1>加一個樣式,變成48號字體,灰色,帶陰影:

<html>

<head>

  <title>Hello</title>

  <style>

    h1 {

      color: #333333;

      font-size: 48px;

      text-shadow: 3px 3px 3px #666666;

    }

  </style>

</head>

<body>

  <h1>Hello, world!</h1>

</body>

</html>

效果如下:

hello-css

3、JavaScript簡介

JavaScript雖然名稱有個Java,但它和Java真的一點關係沒有。JavaScript是爲了讓HTML具有交互性而作爲腳本語言添加的,JavaScript既可以內嵌到HTML中,也可以從外部鏈接到HTML中。如果我們希望當用戶點擊標題時把標題變成紅色,就必須通過JavaScript來實現:

<html>

<head>

  <title>Hello</title>

  <style>

    h1 {

      color: #333333;

      font-size: 48px;

      text-shadow: 3px 3px 3px #666666;

    }

  </style>

  <script>

    function change() {

      document.getElementsByTagName('h1')[0].style.color = '#ff0000';

    }

  </script>

</head>

<body>

  <h1 onclick="change()">Hello, world!</h1>

</body>

</html>

點擊標題後效果如下:

hello-js-change-color

小結

如果要學習Web開發,首先要對HTML、CSS和JavaScript作一定的瞭解。HTML定義了頁面的內容,CSS來控制頁面元素的樣式,而JavaScript負責頁面的交互邏輯。

講解HTML、CSS和JavaScript就可以寫3本書,對於優秀的Web開發人員來說,精通HTML、CSS和JavaScript是必須的,這裏推薦一個在線學習網站w3schools:

http://www.w3schools.com/

 

以及一個對應的中文版本:

http://www.w3school.com.cn/

當我們用Python或者其他語言開發Web應用時,我們就是要在服務器端動態創建出HTML,這樣,瀏覽器就會向不同的用戶顯示出不同的Web頁面。

 

21.3 WSG接口

瞭解了HTTP協議和HTML文檔,我們其實就明白了一個Web應用的本質就是:

1)瀏覽器發送一個HTTP請求;

2)服務器收到請求,生成一個HTML文檔;

3)服務器把HTML文檔作爲HTTP響應的Body發送給瀏覽器;

4)瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。

所以,最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。Apache、Nginx、Lighttpd等這些常見的靜態服務器就是幹這件事情的。

如果要動態生成HTML,就需要把上述步驟自己來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,如果我們自己來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。

正確的做法是底層代碼由專門的服務器軟件實現,我們用Python專注於生成HTML文檔。因爲我們不希望接觸到TCP連接、HTTP原始請求和響應格式,所以,需要一個統一的接口,讓我們專心用Python編寫Web業務。

這個接口就是WSGI:Web Server Gateway Interface。

WSGI接口定義非常簡單,它只要求Web開發者實現一個函數,就可以響應HTTP請求。我們來看一個最簡單的Web版本的“Hello, web!”:

def application(environ, start_response):

    start_response('200 OK', [('Content-Type', 'text/html')])

     return [b'<h1>Hello, web!</h1>']

上面的application()函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數:

environ:一個包含所有HTTP請求信息的dict對象;

start_response:一個發送HTTP響應的函數。

application()函數中,調用:

start_response('200 OK', [('Content-Type', 'text/html')])

就發送了HTTP響應的Header,注意Header只能發送一次,也就是隻能調用一次start_response()函數。start_response()函數接收兩個參數,一個是HTTP響應碼,一個是一組list表示的HTTP Header,每個Header用一個包含兩個str的tuple表示。

通常情況下,都應該把Content-Type頭髮送給瀏覽器。其他很多常用的HTTP Header也應該發送。

然後,函數的返回值b'<h1>Hello, web!</h1>'將作爲HTTP響應的Body發送給瀏覽器。

有了WSGI,我們關心的就是如何從environ這個dict對象拿到HTTP請求信息,然後構造HTML,通過start_response()發送Header,最後返回Body。

整個application()函數本身沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不需要我們自己編寫,我們只負責在更高層次上考慮如何響應請求就可以了。

不過,等等,這個application()函數怎麼調用?如果我們自己調用,兩個參數environ和start_response我們沒法提供,返回的bytes也沒法發給瀏覽器。

所以application()函數必須由WSGI服務器來調用。有很多符合WSGI規範的服務器,我們可以挑選一個來用。但是現在,我們只想儘快測試一下我們編寫的application()函數真的可以把HTML輸出到瀏覽器,所以,要趕緊找一個最簡單的WSGI服務器,把我們的Web應用程序跑起來。

好消息是Python內置了一個WSGI服務器,這個模塊叫wsgiref,它是用純Python編寫的WSGI服務器的參考實現。所謂“參考實現”是指該實現完全符合WSGI標準,但是不考慮任何運行效率,僅供開發和測試使用。

1、運行WSGI服務

我們先編寫hello.py,實現Web應用程序的WSGI處理函數:

# hello.py

def application(environ, start_response):

    start_response('200 OK', [('Content-Type', 'text/html')])

    return [b'<h1>Hello, web!</h1>']

然後,再編寫一個server.py,負責啓動WSGI服務器,加載application()函數:

# server.py

# 從wsgiref模塊導入:

from wsgiref.simple_server import make_server

# 導入我們自己編寫的application函數:

from hello import application

# 創建一個服務器,IP地址爲空,端口是8000,處理函數是application:

httpd = make_server('', 8000, application)

print('Serving HTTP on port 8000...')

# 開始監聽HTTP請求:

httpd.serve_forever()

確保以上兩個文件在同一個目錄下,然後在命令行輸入python server.py來啓動WSGI服務器:

wsgiref-start

注意:如果8000端口已被其他程序佔用,啓動將失敗,請修改成其他端口。

啓動成功後,打開瀏覽器,輸入http://localhost:8000/,就可以看到結果了:

hello-web

在命令行可以看到wsgiref打印的log信息:

wsgiref-log

Ctrl+C終止服務器。

如果你覺得這個Web應用太簡單了,可以稍微改造一下,從environ裏讀取PATH_INFO,這樣可以顯示更加動態的內容:

# hello.py

def application(environ, start_response):

    start_response('200 OK', [('Content-Type', 'text/html')])

    body = '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web')

    return [body.encode('utf-8')]

你可以在地址欄輸入用戶名作爲URL的一部分,將返回Hello, xxx!:

hello-michael

是不是有點Web App的感覺了?

小結

無論多麼複雜的Web應用程序,入口都是一個WSGI處理函數。HTTP請求的所有輸入信息都可以通過environ獲得,HTTP響應的輸出都可以通過start_response()加上函數返回值作爲Body。

複雜的Web應用程序,光靠一個WSGI函數來處理還是太底層了,我們需要在WSGI之上再抽象出Web框架,進一步簡化Web開發。

21.4 使用Web框架

瞭解了WSGI框架,我們發現:其實一個Web App,就是寫一個WSGI的處理函數,針對每個HTTP請求進行響應。

但是如何處理HTTP請求不是問題,問題是如何處理100個不同的URL。

每一個URL可以對應GET和POST請求,當然還有PUT、DELETE等請求,但是我們通常只考慮最常見的GET和POST請求。

一個最簡單的想法是從environ變量裏取出HTTP請求的信息,然後逐個判斷:

def application(environ, start_response):

    method = environ['REQUEST_METHOD']

    path = environ['PATH_INFO']

    if method=='GET' and path=='/':

        return handle_home(environ, start_response)

    if method=='POST' and path='/signin':

        return handle_signin(environ, start_response)

    ...

只是這麼寫下去代碼是肯定沒法維護了。

代碼這麼寫沒法維護的原因是因爲WSGI提供的接口雖然比HTTP接口高級了不少,但和Web App的處理邏輯比,還是比較低級,我們需要在WSGI接口之上能進一步抽象,讓我們專注於用一個函數處理一個URL,至於URL到函數的映射,就交給Web框架來做。

 

由於用Python開發一個Web框架十分容易,所以Python有上百個開源的Web框架。這裏我們先不討論各種Web框架的優缺點,直接選擇一個比較流行的Web框架——Flask來使用。

Flask編寫Web App比WSGI接口簡單(這不是廢話麼,要是比WSGI還複雜,用框架幹嘛?),我們先用pip安裝Flask:

$ pip install flask

然後寫一個app.py,處理3個URL,分別是:

GET /:首頁,返回Home;

GET /signin:登錄頁,顯示登錄表單;

POST /signin:處理登錄表單,顯示登錄結果。

注意噢,同一個URL/signin分別有GET和POST兩種請求,映射到兩個處理函數中。

 

Flask通過Python的裝飾器在內部自動地把URL和函數給關聯起來,所以,我們寫出來的代碼就像這樣:

 

from flask import Flask

from flask import request

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])

def home():

    return '<h1>Home</h1>'

 

@app.route('/signin', methods=['GET'])

def signin_form():

    return '''<form action="/signin" method="post">

              <p><input name="username"></p>

              <p><input name="password" type="password"></p>

              <p><button type="submit">Sign In</button></p>

              </form>'''

@app.route('/signin', methods=['POST'])

def signin():

    # 需要從request對象讀取表單內容:

    if request.form['username']=='admin' and request.form['password']=='password':

        return '<h3>Hello, admin!</h3>'

    return '<h3>Bad username or password.</h3>'

 

if __name__ == '__main__':

    app.run()

運行python app.py,Flask自帶的Server在端口5000上監聽:

$ python app.py

 * Running on http://127.0.0.1:5000/

打開瀏覽器,輸入首頁地址http://localhost:5000/:

 

flask-home

首頁顯示正確!

再在瀏覽器地址欄輸入http://localhost:5000/signin,會顯示登錄表單:

flask-signin-form

輸入預設的用戶名admin和口令password,登錄成功:

flask-signin-ok

輸入其他錯誤的用戶名和口令,登錄失敗:

flask-signin-failed

實際的Web App應該拿到用戶名和口令後,去數據庫查詢再比對,來判斷用戶是否能登錄成功。

除了Flask,常見的Python Web框架還有:

Django:全能型Web框架;

web.py:一個小巧的Web框架;

Bottle:和Flask類似的Web框架;

Tornado:Facebook的開源異步Web框架。

當然了,因爲開發Python的Web框架也不是什麼難事,我們後面也會講到開發Web框架的內容。

【小結】

有了Web框架,我們在編寫Web應用時,注意力就從WSGI處理函數轉移到URL+對應的處理函數,這樣,編寫Web App就更加簡單了。

在編寫URL處理函數時,除了配置URL外,從HTTP請求拿到用戶數據也是非常重要的。Web框架都提供了自己的API來實現這些功能。Flask通過request.form['name']來獲取表單的內容。

 

21.5 使用模板

Web框架把我們從WSGI中拯救出來了。現在,我們只需要不斷地編寫函數,帶上URL,就可以繼續Web App的開發了。

但是,Web App不僅僅是處理邏輯,展示給用戶的頁面也非常重要。在函數中返回一個包含HTML的字符串,簡單的頁面還可以,但是,想想新浪首頁的6000多行的HTML,你確信能在Python的字符串中正確地寫出來麼?反正我是做不到。

俗話說得好,不懂前端的Python工程師不是好的產品經理。有Web開發經驗的同學都明白,Web App最複雜的部分就在HTML頁面。HTML不僅要正確,還要通過CSS美化,再加上覆雜的JavaScript腳本來實現各種交互和動畫效果。總之,生成HTML頁面的難度很大。

由於在Python代碼裏拼字符串是不現實的,所以,模板技術出現了。

使用模板,我們需要預先準備一個HTML文檔,這個HTML文檔不是普通的HTML,而是嵌入了一些變量和指令,然後,根據我們傳入的數據,替換後,得到最終的HTML,發送給用戶:

mvc-seq

這就是傳說中的MVC:Model-View-Controller,中文名“模型-視圖-控制器”。

Python處理URL的函數就是C:Controller,Controller負責業務邏輯,比如檢查用戶名是否存在,取出用戶信息等等;

包含變量{{ name }}的模板就是V:View,View負責顯示邏輯,通過簡單地替換一些變量,View最終輸出的就是用戶看到的HTML。

MVC中的Model在哪?Model是用來傳給View的,這樣View在替換變量的時候,就可以從Model中取出相應的數據。

上面的例子中,Model就是一個dict:

{ 'name': 'Michael' }

只是因爲Python支持關鍵字參數,很多Web框架允許傳入關鍵字參數,然後,在框架內部組裝出一個dict作爲Model。

 

現在,我們把上次直接輸出字符串作爲HTML的例子用高端大氣上檔次的MVC模式改寫一下:

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])

def home():

    return render_template('home.html')

@app.route('/signin', methods=['GET'])

def signin_form():

    return render_template('form.html')

@app.route('/signin', methods=['POST'])

def signin():

    username = request.form['username']

    password = request.form['password']

    if username=='admin' and password=='password':

        return render_template('signin-ok.html', username=username)

    return render_template('form.html', message='Bad username or password', username=username)

if __name__ == '__main__':

    app.run()

Flask通過render_template()函數來實現模板的渲染。和Web框架類似,Python的模板也有很多種。Flask默認支持的模板是jinja2,所以我們先直接安裝jinja2:

$ pip install jinja2

然後,開始編寫jinja2模板:

home.html

用來顯示首頁的模板:

<html>

<head>

  <title>Home</title>

</head>

<body>

  <h1 style="font-style:italic">Home</h1>

</body>

</html>

form.html

用來顯示登錄表單的模板:

<html>

<head>

  <title>Please Sign In</title>

</head>

<body>

  {% if message %}

  <p style="color:red">{{ message }}</p>

  {% endif %}

  <form action="/signin" method="post">

    <legend>Please sign in:</legend>

    <p><input name="username" placeholder="Username" value="{{ username }}"></p>

    <p><input name="password" placeholder="Password" type="password"></p>

    <p><button type="submit">Sign In</button></p>

  </form>

</body>

</html>

signin-ok.html

登錄成功的模板:

<html>

<head>

  <title>Welcome, {{ username }}</title>

</head>

<body>

  <p>Welcome, {{ username }}!</p>

</body>

</html>

登錄失敗的模板呢?我們在form.html中加了一點條件判斷,把form.html重用爲登錄失敗的模板。

最後,一定要把模板放到正確的templates目錄下,templates和app.py在同級目錄下:

mvc-dir

啓動python app.py,看看使用模板的頁面效果:

mvc-form

通過MVC,我們在Python代碼中處理M:Model和C:Controller,而V:View是通過模板處理的,這樣,我們就成功地把Python代碼和HTML代碼最大限度地分離了。

使用模板的另一大好處是,模板改起來很方便,而且,改完保存後,刷新瀏覽器就能看到最新的效果,這對於調試HTML、CSS和JavaScript的前端工程師來說實在是太重要了。

Jinja2模板中,我們用{{ name }}表示一個需要替換的變量。很多時候,還需要循環、條件判斷等指令語句,在Jinja2中,用{% ... %}表示指令。

比如循環輸出頁碼:

{% for i in page_list %}

    <a href="/page/{{ i }}">{{ i }}</a>

{% endfor %}

如果page_list是一個list:[1, 2, 3, 4, 5],上面的模板將輸出5個超鏈接。

除了Jinja2,常見的模板還有:

Mako:用<% ... %>和${xxx}的一個模板;

Cheetah:也是用<% ... %>和${xxx}的一個模板;

Django:Django是一站式框架,內置一個用{% ... %}和{{ xxx }}的模板。

【小結】

有了MVC,我們就分離了Python代碼和HTML代碼。HTML代碼全部放到模板裏,寫起來更有效率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章