django框架介紹安裝-自寫框架

 

 

 

 原文連接:https://www.cnblogs.com/maple-shaw/p/8862330.htmlhtml

Web框架本質

咱們能夠這樣理解:全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣咱們就能夠本身實現Web框架了。python

import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 8000))
sk.listen()

while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    conn.send(b"OK")
    conn.close()
View Code

sk.bind(("127.0.0.1", 8000))   當綁定爲8000的時候,瀏覽器報錯;ERR_INVALID_HTTP_RESPONSE。無效的響應,響應格式有問題mysql

 

 

socket服務端 

  1. import socket  
  2.   
  3. sk = socket.socket()  
  4. sk.bind(("127.0.0.1", 8000))  
  5. sk.listen()  
  6.   
  7.   
  8. while True:  
  9.     conn, addr = sk.accept()  
  10.     data = conn.recv(8096)  
  11.     conn.send(b"OK")  
  12.     conn.close()  

若是將8000端口改成80,就能瀏覽器就能顯示出ok,可是咱們想要的是其它端口也能顯示內容web

 

能夠說Web服務本質上都是在這十幾行代碼基礎上擴展出來的。這段代碼就是它們的祖宗。sql

用戶在瀏覽器中輸入網址,瀏覽器會向服務端發送數據,那瀏覽器會發送什麼數據?怎麼發?這個誰來定? 你這個網站是這個規定,他那個網站按照他那個規定,那互聯網還能玩麼?數據庫

因此,必須有一個統一的規則,讓你們發送消息、接收消息的時候都有個格式依據,不能隨便寫。django

這個規則就是HTTP協議,之後瀏覽器發送請求信息也好,服務器回覆響應信息也罷,都要按照這個規則來。瀏覽器

HTTP協議主要規定了客戶端和服務器之間的通訊格式,那HTTP協議是怎麼規定消息格式的呢?服務器

讓咱們首先打印下咱們在服務端接收到的消息是什麼。session

  1. import socket    
  2.     
  3. sk = socket.socket()    
  4. sk.bind(("127.0.0.1", 8000))    
  5. sk.listen()    
  6.     
  7.     
  8. while True:    
  9.     conn, addr = sk.accept()    
  10.     data = conn.recv(8096)    
  11.     print(data)  # 將瀏覽器發來的消息打印出來    
  12.     conn.send(b"OK")    
  13.     conn.close()    

輸出:

接收回來的是bytes類型,打印解碼的爲(字符串類型,換行了):

print(data.decode()) # 將瀏覽器發來的消息打印出來

 

複製代碼
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8\r\n\r\n'
複製代碼

咱們將\r\n替換成換行看得更清晰點:

  1. GET / HTTP/1.1  
  2. Host: 127.0.0.1:8080  
  3. Connection: keep-alive  
  4. Cache-Control: max-age=0  
  5. Upgrade-Insecure-Requests: 1  
  6. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3355.4 Safari/537.36  
  7. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8  
  8. Accept-Encoding: gzip, deflate, br  
  9. Accept-Language: zh-CN,zh;q=0.9  
  10. Cookie: csrftoken=CtHePYARJOKNx5oNVwxIteOJXpNyJ29L4bW4506YoVqFaIFFaHm0EWDZqKmw6Jm8  
  11.   
  12.   

而後咱們再看一下咱們訪問博客園官網時瀏覽器收到的響應信息是什麼。

響應相關信息能夠在瀏覽器調試窗口的Network標籤頁中看到。響應數據不是都能看的view sourse

點擊view source以後顯示以下圖:

 

response是請求的數據體部分,複製粘貼到本身建立的socket裏,讓它發送給瀏覽器

當發送給瀏覽器的爲HTML時:conn.send(b"HTTP/1.1 200 OK\r\n\r\n <h1>ok</h1> ")。渲染了

 

 

咱們發現收發的消息須要按照必定的格式來,這裏就須要瞭解一下HTTP協議了。

HTTP協議介紹

HTTP協議對收發消息的格式要求

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

HTTP響應的Header中有一個 Content-Type代表響應的內容格式。它的值如text/html; charset=utf-8。

text/html則表示是網頁,charset=utf-8則表示編碼爲utf-8。

HTTP GET請求的格式:

HTTP響應的格式:

 自定義web框架

通過上面的學習,那咱們基於socket服務端的十幾行代碼寫一個咱們本身的web框架。咱們先不處理瀏覽器發送的請求,先讓瀏覽器能顯示咱們web框架返回的信息,那咱們就要按照HTTP協議的格式來發送響應。

  1. import socket    
  2.     
  3. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    
  4. sock.bind(('127.0.0.1', 8000))    
  5. sock.listen()    
  6.     
  7. while True:    
  8.     conn, addr = sock.accept()    
  9.     data = conn.recv(8096)    
  10.     # 給回覆的消息加上響應狀態行    
  11.     conn.send(b"HTTP/1.1 200 OK\r\n\r\n")    
  12.     conn.send(b"OK")    
  13.     conn.close()    

咱們經過十幾行代碼簡單地演示了web 框架的本質。

接下來就讓咱們繼續完善咱們的自定義web框架吧!

根據不一樣的路徑返回不一樣的內容

 

 

這樣就結束了嗎? 如何讓咱們的Web服務根據用戶請求的URL不一樣而返回不一樣的內容呢? 

小事一樁,咱們能夠從請求相關數據裏面拿到請求URL的路徑,而後拿路徑作一個判斷...

  1. """ 
  2. 根據URL中不一樣的路徑返回不一樣的內容 
  3. """  
  4.   
  5. import socket  
  6.   
  7. sk = socket.socket()  
  8. sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口  
  9. sk.listen()  # 監聽  
  10.   
  11. while True:  
  12.     # 等待鏈接  
  13.     conn, add = sk.accept()  
  14.     data = conn.recv(8096)  # 接收客戶端發來的消息  
  15.     # 從data中取到路徑  
  16.     data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串  
  17.     # 按\r\n分割  
  18.     data1 = data.split("\r\n")[0]  
  19.     url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑  
  20.     conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行  
  21.     # 根據不一樣的路徑返回不一樣內容  
  22.     if url == "/index/":  
  23.         response = b"index"  
  24.     elif url == "/home/":  
  25.         response = b"home"  
  26.     else:  
  27.         response = b"404 not found!"  
  28.   
  29.     conn.send(response)  
  30.     conn.close()  

當瀏覽器訪問對應的目錄時,後臺顯示/home,咱們要根據訪問的目錄而send給瀏覽器不一樣的數據。

獲取路徑:

data = conn.recv(8096)
print(data.decode().split()) # 將瀏覽器發來的消息打印出來

 

根據索引打印

print(data.decode().split()[1])  # 將瀏覽器發來的消息打印出來

favicon.ico不用管,是谷歌瀏覽器 的title

由於獲取到的有斜線,而這裏判斷是沒有斜線的字符串,因此麼有匹配成功。正則的話有米有都能實現也能夠的。

 先發請求頭,再發數據,分兩次發。

根據不一樣的路徑返回不一樣的內容--函數版

上面的代碼解決了不一樣URL路徑返回不一樣內容的需求。

咱們返回的內容是簡單的幾個字符,那若是我能夠將返回的結果封裝成一個函數呢?

  1. """ 
  2. 根據URL中不一樣的路徑返回不一樣的內容--函數版 
  3. """  
  4.   
  5. import socket  
  6.   
  7. sk = socket.socket()  
  8. sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口  
  9. sk.listen()  # 監聽  
  10.   
  11.   
  12. # 將返回不一樣的內容部分封裝成函數  
  13. def func(url):  
  14.     s = "這是{}頁面!".format(url)  
  15.     return bytes(s, encoding="utf8")  
  16.   
  17.   
  18. while True:  
  19.     # 等待鏈接  
  20.     conn, add = sk.accept()  
  21.     data = conn.recv(8096)  # 接收客戶端發來的消息  
  22.     # 從data中取到路徑  
  23.     data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串  
  24.     # 按\r\n分割  
  25.     data1 = data.split("\r\n")[0]  
  26.     url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑  
  27.     conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行  
  28.     # 根據不一樣的路徑返回不一樣內容,response是具體的響應體  
  29.     if url == "/index/":  
  30.         response = func(url)  
  31.     elif url == "/home/":  
  32.         response = func(url)  
  33.     else:  
  34.         response = b"404 not found!"  
  35.   
  36.     conn.send(response)  
  37.     conn.close()  

def func(url):
    s = "this is {} page !".format(url)
    return bytes(s, encoding="utf8")

 

根據不一樣的路徑返回不一樣的內容--函數進階版

看起來上面的代碼寫了一個函數,那確定能夠寫多個函數,不一樣的路徑對應執行不一樣的函數拿到結果,可是咱們要一個個判斷路徑,是否是很麻煩?咱們有簡單的辦法來解決。

  1. """ 
  2. 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 
  3. """  
  4.   
  5. import socket  
  6.   
  7. sk = socket.socket()  
  8. sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口  
  9. sk.listen()  # 監聽  
  10.   
  11.   
  12. # 將返回不一樣的內容部分封裝成不一樣的函數  
  13. def index(url):  
  14.     s = "這是{}頁面XX!".format(url)  
  15.     return bytes(s, encoding="utf8")  
  16.   
  17.   
  18. def home(url):  
  19.     s = "這是{}頁面。。!".format(url)  
  20.     return bytes(s, encoding="utf8")  
  21.   
  22.   
  23. # 定義一個url和實際要執行的函數的對應關係  
  24. list1 = [  
  25.     ("/index/", index),  
  26.     ("/home/", home),  
  27. ]  
  28.   
  29. while True:  
  30.     # 等待鏈接  
  31.     conn, add = sk.accept()  
  32.     data = conn.recv(8096)  # 接收客戶端發來的消息  
  33.     # 從data中取到路徑  
  34.     data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串  
  35.     # 按\r\n分割  
  36.     data1 = data.split("\r\n")[0]  
  37.     url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑  
  38.     conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行  
  39.     # 根據不一樣的路徑返回不一樣內容  
  40.     func = None  # 定義一個保存將要執行的函數名的變量  
  41.     for item in list1:  
  42.         if item[0] == url:  
  43.             func = item[1]  
  44.             break  
  45.     if func:  
  46.         response = func(url)  
  47.     else:  
  48.         response = b"404 not found!"  
  49.   
  50.     # 返回具體的響應消息  
  51.     conn.send(response)  
  52.     conn.close()  

分析:

1)定義多個函數,將取出url後面的訪問目錄傳入到函數中,執行函數。
2)定義一個列表,列表中有不少個字符串和函數名對應的元組
3)定義一個變量爲None,循環列表元素,判斷元素第一個字元素是否爲url中取出的的訪問文件,是就讓變量從新賦值爲這個這個元素的第二個字元素,即這個變量等於4一個定義好的函數名。
4)若是func是真,訪問url中的匹配成功,那麼就執行這個函數並用返回數據變量接收函數返回值,不然讓請求返回的變量是404未找到。
5)而後發送請求返回的數據給瀏覽器。這樣就實現了一個框架。

返回具體的HTML文件

完美解決了不一樣URL返回不一樣內容的問題。 可是我不想僅僅返回幾個字符串,我想給瀏覽器返回完整的HTML內容,這又該怎麼辦呢?

沒問題,不論是什麼內容,最後都是轉換成字節數據發送出去的。 咱們能夠打開HTML文件,讀取出它內部的二進制數據,而後再發送給瀏覽器。

  1. """ 
  2. 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 
  3. 返回獨立的HTML頁面 
  4. """  
  5.   
  6. import socket  
  7.   
  8. sk = socket.socket()  
  9. sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口  
  10. sk.listen()  # 監聽  
  11.   
  12.   
  13. # 將返回不一樣的內容部分封裝成不一樣的函數  
  14. def index(url):  
  15.     # 讀取index.html頁面的內容  
  16.     with open("index.html", "r", encoding="utf8") as f:  
  17.         s = f.read()  
  18.     # 返回字節數據  
  19.     return bytes(s, encoding="utf8")  
  20.   
  21.   
  22. def home(url):  
  23.     with open("home.html", "r", encoding="utf8") as f:  
  24.         s = f.read()  
  25.     return bytes(s, encoding="utf8")  
  26.   
  27.   
  28. # 定義一個url和實際要執行的函數的對應關係  
  29. list1 = [  
  30.     ("/index/", index),  
  31.     ("/home/", home),  
  32. ]  
  33.   
  34. while True:  
  35.     # 等待鏈接  
  36.     conn, add = sk.accept()  
  37.     data = conn.recv(8096)  # 接收客戶端發來的消息  
  38.     # 從data中取到路徑  
  39.     data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串  
  40.     # 按\r\n分割  
  41.     data1 = data.split("\r\n")[0]  
  42.     url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑  
  43.     conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行  
  44.     # 根據不一樣的路徑返回不一樣內容  
  45.     func = None  # 定義一個保存將要執行的函數名的變量  
  46.     for item in list1:  
  47.         if item[0] == url:  
  48.             func = item[1]  
  49.             break  
  50.     if func:  
  51.         response = func(url)  
  52.     else:  
  53.         response = b"404 not found!"  
  54.   
  55.     # 返回具體的響應消息  
  56.     conn.send(response)  
  57.     conn.close()  
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>index</title>
</head>
<body>
<div>這是index頁面</div>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>index</title>
</head>
<body>
<div>這是home頁面</div>
</body>
</html>
home.html

1)將以前的請求過來執行的函數中,函數的返回值由字符串改成返回對應html的文件內容。
2)實現方法是在函數中將文件內容都讀取出來,而後由以前的函數中定義的字符串改成返回讀取出來的文件數據。
3)後面程序與上的相同,這樣就實現了根據不一樣url執行不一樣的函數,返回不一樣的數據給瀏覽器:
  2)定義一個列表,列表中有不少個字符串和函數名對應的元組
  3)定義一個變量爲None,循環列表元素,判斷元素第一個字元素是否爲url中取出的的訪問文件,是就讓變量從新賦值爲這個這個元素的第二個字元素,即這個變量等於4一個定義好的函數名。
  4)若是func是真,訪問url中的匹配成功,那麼就執行這個函數並用返回數據變量接收函數返回值,不然讓請求返回的變量是404未找到。
  5)而後發送請求返回的數據給瀏覽器。這樣就實現了一個框架。

 

讓網頁動態起來

這網頁可以顯示出來了,可是都是靜態的啊。頁面的內容都不會變化的,我想要的是動態網站。

沒問題,我也有辦法解決。我選擇使用字符串替換來實現這個需求。(這裏使用時間戳來模擬動態的數據)

  1. """ 
  2. 根據URL中不一樣的路徑返回不一樣的內容--函數進階版 
  3. 返回獨立的HTML頁面 
  4. """  
  5.   
  6. import socket  
  7.   
  8. sk = socket.socket()  
  9. sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口  
  10. sk.listen()  # 監聽  
  11.   
  12.   
  13. # 將返回不一樣的內容部分封裝成不一樣的函數  
  14. def index(url):  
  15.     # 讀取index.html頁面的內容  
  16.     with open("index.html", "r", encoding="utf8") as f:  
  17.         s = f.read()  
  18.     # 返回字節數據  
  19.     return bytes(s, encoding="utf8")  
  20.   
  21.   
  22. def home(url):  
  23.     with open("home.html", "r", encoding="utf8") as f:  
  24.         s = f.read()  
  25.     return bytes(s, encoding="utf8")  
  26.   
  27.   
  28. def timer(url):  
  29.     import time  
  30.     with open("time.html", "r", encoding="utf8") as f:  
  31.         s = f.read()  
  32.         s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S"))  
  33.     return bytes(s, encoding="utf8")  
  34.   
  35.   
  36. # 定義一個url和實際要執行的函數的對應關係  
  37. list1 = [  
  38.     ("/index/", index),  
  39.     ("/home/", home),  
  40.     ("/time/", timer),  
  41. ]  
  42.   
  43. while True:  
  44.     # 等待鏈接  
  45.     conn, add = sk.accept()  
  46.     data = conn.recv(8096)  # 接收客戶端發來的消息  
  47.     # 從data中取到路徑  
  48.     data = str(data, encoding="utf8")  # 把收到的字節類型的數據轉換成字符串  
  49.     # 按\r\n分割  
  50.     data1 = data.split("\r\n")[0]  
  51.     url = data1.split()[1]  # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑  
  52.     conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行  
  53.     # 根據不一樣的路徑返回不一樣內容  
  54.     func = None  # 定義一個保存將要執行的函數名的變量  
  55.     for item in list1:  
  56.         if item[0] == url:  
  57.             func = item[1]  
  58.             break  
  59.     if func:  
  60.         response = func(url)  
  61.     else:  
  62.         response = b"404 not found!"  
  63.   
  64.     # 返回具體的響應消息  
  65.     conn.send(response)  
  66.     conn.close()  
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>time</title>
</head>
<body>
<div>當前時間是:@@time@@</div>
</body>
</html>
View Code
<div>當前時間是:@@time@@</div>   在代碼中將文件讀取出來,而後字符串替換成當前的時間,而後做爲函數返回值編碼發送給訪問這個地址的請求。實現網頁中數據不固定,用數據渲染顯示頁面。

 

服務器程序和應用程序

對於真實開發中的python web程序來講,通常會分爲兩部分:服務器程序和應用程序。

服務器程序負責對socket服務端進行封裝,並在請求到來時,對請求的各類數據進行整理。

應用程序則負責具體的邏輯處理。爲了方便應用程序的開發,就出現了衆多的Web框架,例如:Django、Flask、web.py 等。不一樣的框架有不一樣的開發方式,可是不管如何,開發出的應用程序都要和服務器程序配合,才能爲用戶提供服務。

 

這樣,服務器程序就須要爲不一樣的框架提供不一樣的支持。這樣混亂的局面不管對於服務器仍是框架,都是很差的。對服務器來講,須要支持各類不一樣框架,對框架來講,只有支持它的服務器才能被開發出的應用使用。

這時候,標準化就變得尤其重要。咱們能夠設立一個標準,只要服務器程序支持這個標準,框架也支持這個標準,那麼他們就能夠配合使用。一旦標準肯定,雙方各自實現。這樣,服務器能夠支持更多支持標準的框架,框架也可使用更多支持標準的服務器。

WSGI(Web Server Gateway Interface)就是一種規範,它定義了使用Python編寫的web應用程序與web服務器程序之間的接口格式,實現web應用程序與web服務器程序間的解耦。

經常使用的WSGI服務器有uWSGI、Gunicorn。而Python標準庫提供的獨立WSGI服務器叫wsgiref,Django開發環境用的就是這個模塊來作服務器。

 

wsgiref

咱們利用wsgiref模塊來替換咱們本身寫的web框架的socket server部分:

  1. """  
  2. 根據URL中不一樣的路徑返回不一樣的內容--函數進階版  
  3. 返回HTML頁面  
  4. 讓網頁動態起來  
  5. wsgiref模塊版  
  6. """   
  7.      
  8. from wsgiref.simple_server import make_server   
  9.      
  10.      
  11. # 將返回不一樣的內容部分封裝成函數   
  12. def index(url):   
  13.     # 讀取index.html頁面的內容   
  14.     with open("index.html", "r", encoding="utf8") as f:   
  15.         s = f.read()   
  16.     # 返回字節數據   
  17.     return bytes(s, encoding="utf8")   
  18.      
  19.      
  20. def home(url):   
  21.     with open("home.html", "r", encoding="utf8") as f:   
  22.         s = f.read()   
  23.     return bytes(s, encoding="utf8")   
  24.      
  25.      
  26. def timer(url):   
  27.     import time   
  28.     with open("time.html", "r", encoding="utf8") as f:   
  29.         s = f.read()   
  30.         s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S"))   
  31.     return bytes(s, encoding="utf8")   
  32.      
  33.      
  34. # 定義一個url和實際要執行的函數的對應關係   
  35. list1 = [   
  36.     ("/index/", index),   
  37.     ("/home/", home),   
  38.     ("/time/", timer),   
  39. ]   
  40.      
  41.      
  42. def run_server(environ, start_response):   
  43.     start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 設置HTTP響應的狀態碼和頭信息   
  44.     url = environ['PATH_INFO']  # 取到用戶輸入的url   
  45.     func = None   
  46.     for i in list1:   
  47.         if i[0] == url:   
  48.             func = i[1]   
  49.             break   
  50.     if func:   
  51.         response = func(url)   
  52.     else:   
  53.         response = b"404 not found!"   
  54.     return [response, ]   
  55.      
  56.      
  57. if __name__ == '__main__':   
  58.     httpd = make_server('127.0.0.1', 8090, run_server)   
  59.     print("我在8090等你哦...")   
  60.     httpd.serve_forever()  

{
    'ALLUSERSPROFILE': 'C:\\ProgramData',
    'APPDATA': 'C:\\Users\\Administrator\\AppData\\Roaming',
    'COMMONPROGRAMFILES': 'C:\\Program Files (x86)\\Common Files',
    'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
    'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
    'COMPUTERNAME': 'PC-20190328RVNA',
    'COMSPEC': 'C:\\Windows\\system32\\cmd.exe',
    'FP_NO_HOST_CHECK': 'NO',
    'HOMEDRIVE': 'C:',
    'HOMEPATH': '\\Users\\Administrator',
    'LOCALAPPDATA': 'C:\\Users\\Administrator\\AppData\\Local',
    'LOGONSERVER': '\\\\PC-20190328RVNA',
    'NUMBER_OF_PROCESSORS': '4',
    'OS': 'Windows_NT',
    'PATH': 'C:\\mcw\\venv\\Scripts;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\軟件安裝\\Git\\cmd;C:\\mysql\\mysql-5.6.44-winx64\\bin;C:\\python3\\Scripts\\;C:\\python3\\',
    'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
    'PROCESSOR_ARCHITECTURE': 'x86',
    'PROCESSOR_ARCHITEW6432': 'AMD64',
    'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 58 Stepping 9, GenuineIntel',
    'PROCESSOR_LEVEL': '6',
    'PROCESSOR_REVISION': '3a09',
    'PROGRAMDATA': 'C:\\ProgramData',
    'PROGRAMFILES': 'C:\\Program Files (x86)',
    'PROGRAMFILES(X86)': 'C:\\Program Files (x86)',
    'PROGRAMW6432': 'C:\\Program Files',
    'PROMPT': '(venv) $P$G',
    'PSMODULEPATH': 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
    'PUBLIC': 'C:\\Users\\Public',
    'PYCHARM_HOSTED': '1',
    'PYCHARM_MATPLOTLIB_PORT': '59585',
    'PYTHONIOENCODING': 'UTF-8',
    'PYTHONPATH': 'C:\\mcw;C:\\軟件安裝\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend',
    'PYTHONUNBUFFERED': '1',
    'SESSIONNAME': 'Console',
    'SYSTEMDRIVE': 'C:',
    'SYSTEMROOT': 'C:\\Windows',
    'TEMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp',
    'TMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp',
    'USERDOMAIN': 'PC-20190328RVNA',
    'USERNAME': 'Administrator',
    'USERPROFILE': 'C:\\Users\\Administrator',
    'VIRTUAL_ENV': 'C:\\mcw\\venv',
    'WINDIR': 'C:\\Windows',
    'WINDOWS_TRACING_FLAGS': '3',
    'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log',
    '_OLD_VIRTUAL_PATH': 'C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\杞\ue219歡瀹夎\ue5ca\\Git\\cmd;C:\\mysql\\mysql-5.6.44-winx64\\bin;C:\\python3\\Scripts\\;C:\\python3\\',
    '_OLD_VIRTUAL_PROMPT': '$P$G',
    'SERVER_NAME': 'PC-20190328RVNA',
    'GATEWAY_INTERFACE': 'CGI/1.1',
    'SERVER_PORT': '8090',
    'REMOTE_HOST': '',
    'CONTENT_LENGTH': '',
    'SCRIPT_NAME': '',
    'SERVER_PROTOCOL': 'HTTP/1.1',
    'SERVER_SOFTWARE': 'WSGIServer/0.2',
    'REQUEST_METHOD': 'GET',
    'PATH_INFO': '/time/',
    'QUERY_STRING': '',
    'REMOTE_ADDR': '127.0.0.1',
    'CONTENT_TYPE': 'text/plain',
    'HTTP_HOST': '127.0.0.1:8090',
    'HTTP_CONNECTION': 'keep-alive',
    'HTTP_CACHE_CONTROL': 'max-age=0',
    'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
    'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
    'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
    'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9',
    'wsgi.input': < _io.BufferedReader name = 564 > ,
    'wsgi.errors': < _io.TextIOWrapper name = '<stderr>'
    mode = 'w'
    encoding = 'UTF-8' > ,
    'wsgi.version': (1, 0),
    'wsgi.run_once': False,
    'wsgi.url_scheme': 'http',
    'wsgi.multithread': True,
    'wsgi.multiprocess': False,
    'wsgi.file_wrapper': < class 'wsgiref.util.FileWrapper' >
} <
bound method BaseHandler.start_response of < wsgiref.simple_server.ServerHandler object at 0x02A06950 >>
    127.0 .0 .1 - -[11 / Jun / 2019 19: 12: 41]
"GET /time/ HTTP/1.1"
200 301 {
    'ALLUSERSPROFILE': 'C:\\ProgramData',
    'APPDATA': 'C:\\Users\\Administrator\\AppData\\Roaming',
    'COMMONPROGRAMFILES': 'C:\\Program Files (x86)\\Common Files',
    'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
    'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
    'COMPUTERNAME': 'PC-20190328RVNA',
    'COMSPEC': 'C:\\Windows\\system32\\cmd.exe',
    'FP_NO_HOST_CHECK': 'NO',
    'HOMEDRIVE': 'C:',
    'HOMEPATH': '\\Users\\Administrator',
    'LOCALAPPDATA': 'C:\\Users\\Administrator\\AppData\\Local',
    'LOGONSERVER': '\\\\PC-20190328RVNA',
    'NUMBER_OF_PROCESSORS': '4',
    'OS': 'Windows_NT',
    'PATH': 'C:\\mcw\\venv\\Scripts;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\軟件安裝\\Git\\cmd;C:\\mysql\\mysql-5.6.44-winx64\\bin;C:\\python3\\Scripts\\;C:\\python3\\',
    'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
    'PROCESSOR_ARCHITECTURE': 'x86',
    'PROCESSOR_ARCHITEW6432': 'AMD64',
    'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 58 Stepping 9, GenuineIntel',
    'PROCESSOR_LEVEL': '6',
    'PROCESSOR_REVISION': '3a09',
    'PROGRAMDATA': 'C:\\ProgramData',
    'PROGRAMFILES': 'C:\\Program Files (x86)',
    'PROGRAMFILES(X86)': 'C:\\Program Files (x86)',
    'PROGRAMW6432': 'C:\\Program Files',
    'PROMPT': '(venv) $P$G',
    'PSMODULEPATH': 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
    'PUBLIC': 'C:\\Users\\Public',
    'PYCHARM_HOSTED': '1',
    'PYCHARM_MATPLOTLIB_PORT': '59585',
    'PYTHONIOENCODING': 'UTF-8',
    'PYTHONPATH': 'C:\\mcw;C:\\軟件安裝\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend',
    'PYTHONUNBUFFERED': '1',
    'SESSIONNAME': 'Console',
    'SYSTEMDRIVE': 'C:',
    'SYSTEMROOT': 'C:\\Windows',
    'TEMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp',
    'TMP': 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp',
    'USERDOMAIN': 'PC-20190328RVNA',
    'USERNAME': 'Administrator',
    'USERPROFILE': 'C:\\Users\\Administrator',
    'VIRTUAL_ENV': 'C:\\mcw\\venv',
    'WINDIR': 'C:\\Windows',
    'WINDOWS_TRACING_FLAGS': '3',
    'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log',
    '_OLD_VIRTUAL_PATH': 'C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\杞\ue219歡瀹夎\ue5ca\\Git\\cmd;C:\\mysql\\mysql-5.6.44-winx64\\bin;C:\\python3\\Scripts\\;C:\\python3\\',
    '_OLD_VIRTUAL_PROMPT': '$P$G',
    'SERVER_NAME': 'PC-20190328RVNA',
    'GATEWAY_INTERFACE': 'CGI/1.1',
    'SERVER_PORT': '8090',
    'REMOTE_HOST': '',
    'CONTENT_LENGTH': '',
    'SCRIPT_NAME': '',
    'SERVER_PROTOCOL': 'HTTP/1.1',
    'SERVER_SOFTWARE': 'WSGIServer/0.2',
    'REQUEST_METHOD': 'GET',
    'PATH_INFO': '/favicon.ico',
    'QUERY_STRING': '',
    'REMOTE_ADDR': '127.0.0.1',
    'CONTENT_TYPE': 'text/plain',
    'HTTP_HOST': '127.0.0.1:8090',
    'HTTP_CONNECTION': 'keep-alive',
    'HTTP_PRAGMA': 'no-cache',
    'HTTP_CACHE_CONTROL': 'no-cache',
    'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
    'HTTP_ACCEPT': 'image/webp,image/apng,image/*,*/*;q=0.8',
    'HTTP_REFERER': 'http://127.0.0.1:8090/time/',
    'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
    'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9',
    'wsgi.input': < _io.BufferedReader name = 564 > ,
    'wsgi.errors': < _io.TextIOWrapper name = '<stderr>'
    mode = 'w'
    encoding = 'UTF-8' > ,
    'wsgi.version': (1, 0),
    'wsgi.run_once': False,
    'wsgi.url_scheme': 'http',
    'wsgi.multithread': True,
    'wsgi.multiprocess': False,
    'wsgi.file_wrapper': < class 'wsgiref.util.FileWrapper' >
}
打印environ並格式化後顯示全部變量

獲取環境變量一個鍵:environ['PATH_INFO']

鍵的值是後面的值,就是url中獲取到的值,: 'PATH_INFO': '/time/',

'PATH': 'C:\\mcw\\venv\\Scripts;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\軟件安裝\\Git\\cmd;C:\\mysql\\mysql-5.6.44-winx64\\bin;C:\\python3\\Scripts\\;C:\\python3\\',

start_response是個對象:<bound method BaseHandler.start_response of <wsgiref.simple_server.ServerHandler object at 0x02A16930>>

 

from wsgiref.simple_server import make_server

def run_server(environ, start_response): #2)定義執行函數,傳參environ, start_response
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 4) 設置HTTP響應的狀態碼和頭信息start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  url = environ['PATH_INFO'] #3)取到用戶輸入的url environ['PATH_INFO'] if url=='/home/': response=("response:"+url).encode('utf-8') else: response = b"404 not found!" #)url知足條件返回內容定義 return [response, ] #6)執行函數返回列表,列表一個元素是返回的內容 if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) #1)make_server 作服務傳ip端口和執行函數 httpd.serve_forever() #啓動這個服務

用wsgiref代替服務端socket建立簡易web框架:

1)make_server 作服務傳ip端口和執行函數
2)定義執行函數,傳參environ, start_response
3)取到用戶輸入的url environ['PATH_INFO']
4) 設置HTTP響應的狀態碼和頭信息start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) 
5)url知足條件返回內容定義
6)執行函數返回列表,列表一個元素是返回的內容

7)啓動這個服務

jinja2

上面的代碼實現了一個簡單的動態,我徹底能夠從數據庫中查詢數據,而後去替換我html中的對應內容,而後再發送給瀏覽器完成渲染。 這個過程就至關於HTML模板渲染數據。 本質上就是HTML內容中利用一些特殊的符號來替換要展現的數據。 我這裏用的特殊符號是我定義的,其實模板渲染有個現成的工具: jinja2

下載jinja2:

pip install jinja2
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
</head>
<body>
    <h1>姓名:{{name}}</h1>
    <h1>愛好:</h1>
    <ul>
        {% for hobby in hobby_list %}
        <li>{{hobby}}</li>
        {% endfor %}
    </ul>
</body>
</html>
index2.html

使用jinja2渲染index2.html文件:

  1. from wsgiref.simple_server import make_server  
  2. from jinja2 import Template  
  3.   
  4.   
  5. def index(url):  
  6.     # 讀取HTML文件內容  
  7.     with open("index2.html", "r", encoding="utf8") as f:  
  8.         data = f.read()  
  9.         template = Template(data)   # 生成模板文件  
  10.         ret = template.render({'name': '小郭吹雪', 'hobby_list': ['敲代碼', '今日頭條', '睡覺']} # 把數據填充到模板中  
  11.     return bytes(ret, encoding="utf8")  
  12.   
  13.   
  14. def home(url):  
  15.     with open("home.html", "r", encoding="utf8") as f:  
  16.         s = f.read()  
  17.     return bytes(s, encoding="utf8")  
  18.   
  19.   
  20. # 定義一個url和實際要執行的函數的對應關係  
  21. list1 = [  
  22.     ("/index/", index),  
  23.     ("/home/", home),  
  24. ]  
  25.   
  26.   
  27. def run_server(environ, start_response):  
  28.     start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 設置HTTP響應的狀態碼和頭信息  
  29.     url = environ['PATH_INFO']  # 取到用戶輸入的url  
  30.     func = None  
  31.     for i in list1:  
  32.         if i[0] == url:  
  33.             func = i[1]  
  34.             break  
  35.     if func:  
  36.         response = func(url)  
  37.     else:  
  38.         response = b"404 not found!"  
  39.     return [response, ]  
  40.   
  41.   
  42. if __name__ == '__main__':  
  43.     httpd = make_server('127.0.0.1', 8090, run_server)  
  44.     print("我在8090等你哦...")  
  45.     httpd.serve_forever()  
jinja2使用方法;

1)從jinja2導入模板類
2)實例化模板類,傳參爲帶特別語法的文件。
3)對象.render(字典),把數據填充到模板中
4)支持{{name}}雙括號中變量替換爲字典鍵的值,for循環建立語句。
  {'name': '小郭吹雪', 'hobby_list': ['敲代碼', '今日頭條', '睡覺']

  雙花中表明變量,字典的鍵:<h1>姓名:{{name}}</h1>
  上下花百分。上for循環字典鍵中列表,下endfor。中間是對for循環的每一個變量生成html。
  {% for hobby in hobby_list %}
  <li>{{hobby}}</li>
  {% endfor %}

 

如今的數據是咱們本身手寫的,那可不能夠從數據庫中查詢數據,來填充頁面呢?

使用pymysql鏈接數據庫:

複製代碼
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select name, age, department_id from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close()
複製代碼

建立一個測試的user表:

CREATE TABLE user(
  id int auto_increment PRIMARY KEY,
  name CHAR(10) NOT NULL,
  hobby CHAR(20) NOT NULL
)engine=innodb DEFAULT charset=UTF8;

模板的原理就是字符串替換,咱們只要在HTML頁面中遵循jinja2的語法規則寫上,其內部就會按照指定的語法進行相應的替換,從而達到動態的返回內容。

Django

Django官網下載頁面

安裝(安裝最新LTS版):

pip3 install django==1.11.20

指定安裝源(清華源):

pip3 install django==1.11.21 -i https://pypi.tuna.tsinghua.edu.cn/simple

pycharm安裝Django,指定版本

 shift右擊 在當前目錄打開cmd

建立一個django項目:

下面的命令建立了一個名爲"mysite"的Django 項目:

django-admin startproject mysite

目錄介紹:

複製代碼
mysite/
├── manage.py  # 管理文件
└── mysite  # 項目目錄
    ├── __init__.py
    ├── settings.py  # 配置
    ├── urls.py  # 路由 --> URL和函數的對應關係
    └── wsgi.py  # runserver命令就使用wsgiref模塊作簡單的web server
複製代碼

pycharm建立,社區版不支持建立django項目。

file->new project->Django

location項目路徑,名稱。

解釋器

虛擬環境:能隔離開各個項目的環境

不寫不自動生成這個目錄

 

 修改配置:

 

 

 

運行Django項目:

默認端口8000麼?

 

python manage.py runserver 127.0.0.1:8000

1.命令行

切換到項目的根目錄下  manage.py

python36 manage.py runserver # http://127.0.0.1:8000/

python36 manage.py runserver 80   #指定端口啓動  在80端口啓動

python36 manage.py runserver 0.0.0.0:80  #0.0.0.0:80 其它主機可訪問,外部主機可訪問

直接點擊啓動關閉

 

python36 manage.py runserver 0.0.0.0:80#而且settings設置爲‘*’,容許全部主機訪問

 

terminate是項目終止,disconnect是斷開鏈接,項目不終止

命令行開啓的能夠crtl + c終止

disconect點擊以後,要中止項目。能夠在資源管理器中殺掉進程

多了個文件:

 若是啓動時報錯哪一個文件的問題,看看是否是啓動錯了,啓動的不是Django項目而是某個文件:

 

 

模板文件配置:

複製代碼
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "template")],  # template文件夾位置
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
複製代碼

靜態文件配置:

STATIC_URL = '/static/'  # HTML中使用的靜態文件夾前綴
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),  # 靜態文件存放位置
]

看不明白?有圖有真相:

剛開始學習時可在配置文件中暫時禁用csrf中間件,方便表單提交測試。

複製代碼
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
複製代碼

Django基礎必備三件套:

from django.shortcuts import HttpResponse, render, redirect

HttpResponse

內部傳入一個字符串參數,返回給瀏覽器。

例如:

def index(request):
    # 業務邏輯代碼
    return HttpResponse("OK")

建立一個頁面

from django.contrib import admin
from django.urls import path

from django.shortcuts import HttpResponse,render
def index(request):
    return HttpResponse('index')
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', index),
]
View Code

 

render

除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數。

將數據填充進模板文件,最後把結果返回給瀏覽器。(相似於咱們上面用到的jinja2)

例如:

def index(request):
    # 業務邏輯代碼
    return render(request, "index.html", {"name": "alex", "hobby": ["燙頭", "泡吧"]})

from django.contrib import admin
from django.urls import path

from django.shortcuts import HttpResponse,render
def index(request):
    # return HttpResponse('index')
    return render(request,'index.html')
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', index),
]
View Code

 

redirect

接受一個URL參數,表示跳轉到指定的URL。

例如:

def index(request):
    # 業務邏輯代碼
    return redirect("/home/")

重定向是怎麼回事?

啓動Django報錯:

Django 啓動時報錯 UnicodeEncodeError ...

報這個錯誤一般是由於計算機名爲中文,改爲英文的計算機名重啓下電腦就能夠了。

 

 

 

相關文章
相關標籤/搜索