- Django實戰:Python Web典型模塊與項目開發
- 張曉
- 4334字
- 2021-01-08 18:09:30
第1章 Python和Web開發框架
Django是一個開放源代碼的Web開發框架,完全用Python開發。它對常用的Web開發模式進行了高度封裝,為常見的編程任務提供了捷徑;通過減少重復的代碼,使程序員能夠專注于Web 應用上的關鍵性的業務開發。因此使用 Django 能在較短的時間內構建并維護質量上乘的Web應用。Django必須運行在Python環境中,可見二者密不可分。
1.1 Python簡介
Python由吉多·范羅蘇姆(Guido van Rossum)創造,Python被設計成一種跨平臺的計算機程序設計語言,是一種面向對象的動態類型語言。自20世紀90年代初Python誕生至今,它已廣泛應用于系統管理的任務處理和Web編程。
Python的設計理念是“優雅”“明確”“簡單”,它是一種功能強大的編程語言,主要有以下特點。
●Python具有解釋型、交互式、面向對象這3個特征。
●Python有極其簡單、明確的語法,關鍵字較少,結構簡單。
●Python可跨平臺,在Linux、Windows和macOS等操作系統中都能很好地運行。
●Python提供所有主流的商業數據庫的接口。
●Python提供了一個很好的結構,支持大型程序開發。
●Python是自由/開放源碼的軟件之一。
1.2 Web開發框架基本知識
Web開發框架是用于Web開發的成套軟件架構。Web開發框架會為Web應用提供成套的功能支持,即一套開發和部署網站的方案。使用Web開發框架,程序員可以只關注業務邏輯代碼的編寫,其他功能使用框架已有的功能即可,這減少了程序員的代碼編寫量。
Web服務本質上是由socket(socket是一種通信機制,通過綁定IP地址和端口產生一個通信鏈,實現計算機間的通信)服務端向socket客戶端提供HTTP響應,而瀏覽器就是一個socket客戶端,它向Web發出請求。Django本身是一個Web開發框架,它連接socket兩端(服務端、客戶端)進行數據交換,當然這種交換按照指定的協議進行,也就是HTTP(Hyper Text Transfer Protocol,超文本傳輸協議)。
1.2.1 Web應用本質
網絡中不同的計算機間進行通信必須經過IP地址和端口。為了降低網絡通信開發的復雜度,人們在TCP/IP 4層結構中的應用層與傳輸層之間加了一層,這個層就是socket層。它把復雜的TCP/IP進行了封裝,并提供了一組服務的接口。
網絡中服務器主機會提供一種或多種服務,每一種服務打開一個socket,并綁定到一個端口上,也就是說不同的端口對應于不同的服務(如Web服務一般用到80端口),客戶端向那個端口發送請求,就會得到相應的服務。
當用戶在瀏覽器地址欄中輸入網址(URL,即Uniform Resource Locator,統一資源定位符)并按下Enter鍵,這個動作稱為發送Web請求,在網絡上會有一臺與網址相對應的服務器按用戶請求做出響應,把請求資源發送給用戶。這臺接收Web請求并做出響應的服務器稱為Web服務器,它把用戶請求的資源以HTML(Hyper Text Markup Language,超文本標記語言)文件的形式傳遞到用戶的瀏覽器中,用戶就看到網頁了。
如上所述,Web應用主要做的事情就是發送HTML文件到瀏覽器,其核心功能則通過socket服務完成。因此,Web服務器本質上是一個socket服務端,而瀏覽器本質上是一個socket客戶端。
以下用代碼來簡單說明Web開發框架的運行方式。
# 導入socket模塊 import socket # 建立socket服務 sk=socket.socket() # 綁定IP與端口號,這是綁定本機端口 sk.bind(('127.0.0.1',8000)) # 進行監聽 sk.listen() print('socket服務開始運行……') while True: # 接收socket客戶端連接 conn,addr=sk.accept() # 接收socket客戶端數據 data=conn.recv(1024) # print(data) # 向客戶端發送消息,字符串前加字母b表示以字節形式傳遞 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 向客戶端發送消息,bytes()函數把字符串轉換成字節形式 conn.send(bytes("我是socket服務端,我已接到你的請求。",encoding='utf-8'))
以上代碼主要實現如下過程。
(1)建立socket服務,綁定IP與端口號,并啟動監聽進程,這樣就把本地計算機設置成socket服務端。
(2)服務啟動后,通過循環語句,持續接收瀏覽器(socket客戶端)發送的信息。
(3)socket服務端與瀏覽器以字節形式在網絡上傳遞消息,在發送字符串前必須將其轉化成字節形式。
(4)socket 服務端與瀏覽器的消息傳遞必須依照HTTP 格式,conn.send(b"HTTP/1.1 200 OK\r\n\r\n")這句代碼把字符串按照HTTP格式向瀏覽器傳遞,主要格式為HTTP/1.1 200 OK,字符串后面跟兩對回車符和換行符“\r\n\r\n”,這樣其后的字符串就能顯示在瀏覽器中。
在命令行終端輸入python test1.py運行代碼啟動socket服務,如圖1.1所示。

圖1.1 啟動socket服務
這時在瀏覽器中輸入http://127.0.0.1:8000/,socket服務端收到請求后,返回相關信息。由于是按照HTTP格式返回信息,所以信息能顯示在瀏覽器上,如圖1.2所示。

圖1.2 瀏覽器顯示socket服務端發回的信息
1.2.2 Web開發框架核心功能
1.2.1 節中的代碼沒有實現根據瀏覽器地址欄中的URL不同而做出不同響應,本節我們對代碼進行改進與完善,實現Web開發框架核心功能,完善后的代碼如下。
import socket def index(url): # 讀取文件,并對占位符進行替換 # with用法:在退出with代碼塊后自動關閉with打開的文件 with open('index.html', 'r',encoding='utf-8') as f: rd = f.read() rd = rd.replace("$@index$@", "首頁") # 替換后的文本以字節形式返回 return bytes(rd,encoding='utf-8') def test(url): with open('test.html', 'r',encoding='utf-8') as f: rd = f.read() rd = rd.replace("$@test$@", "測試") return bytes(rd,encoding='utf-8') def fun404(url): ret = "<h1>not found!</h1>" return bytes(ret, encoding='utf-8') # 定義變量url_func,建立了URL與函數名的對應關系 url_func=[ ("/index/",index), ("/test/",test), ] # 建立socket服務 sk=socket.socket() # 綁定IP與端口號,這里是綁定本機端口 sk.bind(('127.0.0.1',8000)) # 進行監聽 sk.listen() print('socket服務開始運行……') while True: # 接收socket客戶端連接 conn, addr = sk.accept() """ 下面語句中data變量接收socket客戶端(瀏覽器)數據,這個數據有固定格式 數據是HTTP請求數據格式,第一行格式為GET /index/ HTTP/1.1\r\n
該行以\r\n結尾,各字符串以空格分隔
""" data = conn.recv(1024) # 輸出socket服務端接收的瀏覽器發來的消息格式 print(data) if not data: # 如果客戶端沒有發送新的數據,就重新開始,不再向下執行 # 防止后面語句對空字符進行操作而拋出異常 continue # 把收到的數據由字節形式轉換成字符串,一般用到的編碼格式為utf-8 data_str = str(data, encoding='utf-8') # 以\r\n分隔每一行 line = data_str.split("\r\n") # print(line[0]) # 取出第一行字符串(line[0]),然后用空格再次分隔字符串 # 提示:在Django中的索引從0開始 v1 = line[0].split() # 取出路徑,路徑字符串在第2個位置上(以空格分隔) url = v1[1] """ 向客戶端發消息,字符串前加字母b表示以字節形式傳遞 在HTTP/1.1 200 OK\r\n\r\n之后的內容以HTTP格式顯示在瀏覽器中 """ conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func=None """ 用for循環取出url_func中的每一項,它是由URL和函數名組成的元組向客戶端發消息 """ for i in url_func: if i[0] == url: # 取出對應函數名 func = i[1] break if func: func = func else: func = fun404 # 函數名加上括號,表示執行函數 rep = func(url) # 把函數返回的值向客戶端發送 conn.send(rep) conn.close()
上述代碼的相關說明如下。
(1)以上代碼定義了兩個函數——index()和test(),還定義了一個列表類型的變量,列表中每項都是元組,其列出URL與函數名的對應關系。程序流程主要是:根據傳入的參數(URL),讀取相應的HTML文件,并根據占位符(本例中用兩個$@包含一個變量名表示一個占位符,形如$@index$@)進行替換實現網頁動態顯示。
(2)index()函數和test()函數讀取相應的HTML文件并進行占位符替換,同時以字節形式返回替換后的文本,進行替換的HTML文件index.html所含代碼如下,請注意占位符$@index$@的位置。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index頁面</title> </head> <body> <h1>$@index$@</h1> </body> </html>
test.html的代碼與index.html的代碼相似,此處不再列舉。
(3)代碼還增加了一個fun404()函數來處理無對應關系的路徑。
(4)在while True代碼塊中增加了對瀏覽器(socket客戶端)傳來的消息的處理,解析出瀏覽器地址欄中URL的路徑,要正確解析路徑必須了解瀏覽器傳給socket服務端的消息格式。這里以在瀏覽器地址欄中輸入http://127.0.0.1:8000/index/為例,通過print語句可以看到socket服務端收到的消息格式如下。
GET /index/ HTTP/1.1\r\n Host: 127.0.0.1:8000\r\n Connection: keep-alive\r\n User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9\r\n\r\n
可以看到消息格式以\r\n分隔每行,每行中各項再以空格進行分隔,通過這個格式即可理解代碼是如何解析路徑的。
運行python test2.py以進行測試,實現了針對不同請求進行響應的過程,如圖1.3所示。

圖1.3 socket服務端對瀏覽器的不同請求做出不同響應
1.2.3 HTTP簡單介紹
為了使讀者能有效地理解代碼,這里有必要簡單介紹一下HTTP。HTTP就是瀏覽器(客戶端)與Web服務器交流的語言,它是一種雙方都認可的格式或規則,也就是這種語言是雙方都能“聽得懂”的語言,因此協議就是一種格式,這種格式讓雙方都知道對方想表達什么意思、想做什么事。
HTTP消息格式有請求和響應兩種,HTTP請求和響應都包含Header和Body兩部分,其中Body是可選的。
1.2.4 HTTP請求消息格式
HTTP請求(Request)消息包含請求頭(Header)和請求體(Body)。請求頭每行以“\r\n”結尾,請求頭第一行以空格分隔的字符串分別代表請求方法、路徑、HTTP等信息。第二個字符串就是路徑,是一個較為重要的字符串,由此可推知瀏覽器地址欄中的URL。請求頭從第二行開始都是“頭字段名:值\r\n”的形式。請求頭與請求體之間以“\r\n”分隔,請求體可以有也可以沒有。以下是HTTP請求消息格式的示意代碼。
請求方法 路徑 HTTP/1.1\r\n # 請求方法包括GET、POST等 頭字段名:值\r\n 頭字段名:值\r\n … 頭字段名:值\r\n \r\n 請求體 # 請求體可以有,可以沒有
以下是實例代碼,其中,GET 表示請求方式,請求的路徑是/index/,應用的協議是HTTP,協議的版本是1.1。
GET /index/ HTTP/1.1\r\n Host: 127.0.0.1:8000\r\n Connection: keep-alive\r\n User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9\r\n \r\n … # 請求體可以有,可以沒有
1.2.5 HTTP響應消息格式
HTTP響應(Response)消息包含響應頭(Header)和響應正文(Body)。響應頭每行以“\r\n”結尾,響應頭第一行包含代表HTTP、狀態碼和狀態描述符等信息的3個字符串,這3個字符串以空格作為分隔符。響應頭從第二行開始都是“頭字段名:值\r\n”的形式。響應頭與響應正文之間以“\r\n”分隔,響應正文就是顯示在瀏覽器中的HTML格式的內容。以下是HTTP響應消息格式的示意代碼。
HTTP/1.1 狀態碼 狀態描述符\r\n 頭字段名:值\r\n 頭字段名:值\r\n … 頭字段名:值\r\n \r\n 響應正文 # 響應正文,就是HTML格式的內容
以下是實例代碼,其中,HTTP/1.1表示HTTP的版本是1.1,狀態碼200表示響應正常,OK是響應成功的描述字符串,另外還有服務器信息Server、響應時間Date等內容。
HTTP/1.1 200 OK\r\n Server: openresty/1.9.15.1\r\n Date: Fri, 05 Jul 2019 07:58:16 GMT\r\n Content-Type: text/html; charset=utf-8\r\n Transfer-Encoding: chunked\r\n \r\n … # 響應正文
1.3 Python Web開發框架
1.2節我們用Python代碼的形式解釋了如何用socket來實現Web開發框架的流程,Web開發框架的本質就是用HTTP實現socket服務端與瀏覽器的通信功能。這些功能可以概括為3步。
(1)socket服務端與客戶端(瀏覽器)收/發socket消息,按照HTTP來解析消息。
(2)建立URL與要執行的函數的對應關系,這里的函數包含業務邏輯代碼。
(3)載入HTML文件當作模板,對其中特殊符號標識的字符串進行替換并發給瀏覽器顯示。
不理解或看不懂本章關于Web開發框架的代碼對于Django開發影響不太大,讀者不用擔心。對該代碼的理解有利于從Web開發本質與原理層面理解Django,會提高Django開發效率。
Python中的Web框架一般實現3種核心功能。
●收發消息(socket功能)。
●根據用戶不同路徑執行不同的函數。
●從HTML文件中取出內容,并且完成字符串的替換。
目前主流的Python Web開發框架主要有Django、Tornado和Flask這3種。
① Django是目前最流行的Web開發框架之一,該框架包含以上3種核心功能中的第二、第三種功能,這兩種功能可以很容易地通過編寫代碼或配置來實現,第一種功能使用第三方工具實現。Django是Python中最全能的Web開發框架之一,功能完備,在可維護性和開發速度上具有優勢。
② Tornado包含以上3種核心功能,但需要開發人員通過代碼實現。該框架最大的特點是采用異步處理,是非阻塞式、高并發處理框架,性能強大,可以每秒處理數以千計的連接。Tornado是實現實時Web服務的理想框架。其缺點是相比于Django,諸多內容需要開發人員自己去編寫。隨著項目越來越大,Tornado將有越來越多的功能需要開發人員來實現。
③ Flask可實現以上3種核心功能中的第二種功能,第一、第三種功能使用第三方工具實現,是輕量級的開發框架。Flask的特點是使用簡單的核心,并使用插件擴展其他功能,因此Flask是一個面向簡單需求和小型應用的微框架。
1.4 小結
本章簡單介紹了Python的特點,并介紹了Web開發框架基本知識、HTTP以及常見的Python Web開發框架,使讀者對Django的原理有所了解。
- Designing Machine Learning Systems with Python
- The Supervised Learning Workshop
- Mastering Concurrency in Go
- iOS開發實戰:從零基礎到App Store上架
- 機械工程師Python編程:入門、實戰與進階
- Java Web應用開發技術與案例教程(第2版)
- Lua程序設計(第4版)
- CKA/CKAD應試教程:從Docker到Kubernetes完全攻略
- Hands-On Swift 5 Microservices Development
- JavaScript:Moving to ES2015
- Getting Started with NativeScript
- Java EE 8 Application Development
- Clojure Reactive Programming
- UX Design for Mobile
- 網頁設計與制作