Web框架本質
咱們能夠這樣理解:全部的Web應用本質上就是一個socket服務端,而用戶的瀏覽器就是一個socket客戶端。 這樣咱們就能夠本身實現Web框架了。css
總的來講:Web框架的本質就是瀏覽器和服務器基於socket套接字實現請求和響應的過程
半成品自定義web框架
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()
while True:
conn, addr = sk.accept()
data = conn.recv(8096)
conn.send(b"OK")
conn.close()
能夠說Web服務本質上客戶端和服務端基於socket進行的請求和響應的過程。這段代碼就是它們的祖宗。html
用戶的瀏覽器一輸入網址,會給服務端發送數據,那瀏覽器會發送什麼數據?怎麼發?這個誰來定? 你這個網站是這個規定,他那個網站按照他那個規定,這互聯網還能玩麼?前端
因此,必須有一個統一的規則,讓你們發送消息、接收消息的時候有個格式依據,不能隨便寫。python
這個規則就是HTTP協議,之後瀏覽器發送請求信息也好,服務器回覆響應信息也罷,都要按照這個規則來。mysql
HTTP協議主要規定了客戶端和服務器之間的通訊格式,那HTTP協議是怎麼規定消息格式的呢?nginx
讓咱們首先打印下咱們在服務端接收到的消息是什麼。web
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 80))
sk.listen()
while True:
conn, addr = sk.accept()
data = conn.recv(8096)
print(data) # 將瀏覽器發來的消息打印出來
conn.send(b"OK")
conn.close()
輸出:sql
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'
而後咱們再看一下咱們訪問博客園官網時瀏覽器收到的響應信息是什麼。數據庫
響應相關信息能夠在瀏覽器調試窗口的network標籤頁中看到。apache

咱們發現收發的消息須要按照必定的格式來,這裏就須要瞭解一下HTTP協議了。
HTTP協議介紹
HTTP協議對收發消息的格式要求
每一個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。 HTTP響應的Header中有一個 Content-Type
代表響應的內容格式。如 text/html
表示HTML網頁。
HTTP GET請求的格式:
GET /path HTTP/1.1
header1:v1\r\n
header2:v2\r\n
使用 \r\n
分隔多個header
HTTP POST請求格式:
POST /path HTTP/1.1
header1:v1\r\n
header2:v2\r\n
\r\n\r\n
請求體...
當遇到連續兩個 \r\n\r\n
時,表示Header部分結束了,後面的數據是Body。
HTTP響應的格式:
200 OK
Header1:v1\r\n
Header2:v2\r\n
\r\n\r\n
響應體...
處女版自定義web框架
通過上面的補充學習,咱們知道了要想讓咱們本身寫的web server端正經起來,必需要讓咱們的Web server在給客戶端回覆消息的時候按照HTTP協議的規則加上響應狀態行,這樣咱們就實現了一個正經的Web框架了。
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 8000))
sock.listen()
while True:
conn, addr = sock.accept()
data = conn.recv(8096)
# 給回覆的消息加上響應狀態行
conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
conn.send(b"OK")
conn.close()
咱們經過十幾行代碼簡單地演示了web 框架的本質。
接下來就讓咱們繼續完善咱們的自定義web框架吧!
根據不一樣的路徑返回不一樣的內容
這樣就結束了嗎? 如何讓咱們的Web服務根據用戶請求的URL不一樣而返回不一樣的內容呢?
小事一樁,咱們能夠從請求相關數據裏面拿到請求URL的路徑,而後拿路徑作一個判斷...
"""
根據URL中不一樣的路徑返回不一樣的內容
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口
sk.listen() # 監聽
while 1:
# 等待鏈接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客戶端發來的消息
# 從data中取到路徑
data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
# 根據不一樣的路徑返回不一樣內容
if url == "/index/":
response = b"index"
elif url == "/home/":
response = b"home"
else:
response = b"404 not found!"
conn.send(response)
conn.close()
根據不一樣的路徑返回不一樣的內容--函數版
上面的代碼解決了不一樣URL路徑返回不一樣內容的需求。
可是問題又來了,若是有不少不少路徑要判斷怎麼辦?難道要挨個寫if判斷? 固然不用,咱們有更聰明的辦法。
"""
根據URL中不一樣的路徑返回不一樣的內容--函數版
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口
sk.listen() # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url):
s = "這是{}頁面!".format(url)
return bytes(s, encoding="utf8")
def home(url):
s = "這是{}頁面!".format(url)
return bytes(s, encoding="utf8")
while 1:
# 等待鏈接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客戶端發來的消息
# 從data中取到路徑
data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
# 根據不一樣的路徑返回不一樣內容,response是具體的響應體
if url == "/index/":
response = index(url)
elif url == "/home/":
response = home(url)
else:
response = b"404 not found!"
conn.send(response)
conn.close()
根據不一樣的路徑返回不一樣的內容--函數進階版
看起來上面的代碼仍是要挨個寫if判斷,怎麼辦?咱們仍是有辦法!(只要思想不滑坡,方法總比問題多!)
"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口
sk.listen() # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url):
s = "這是{}頁面!".format(url)
return bytes(s, encoding="utf8")
def home(url):
s = "這是{}頁面!".format(url)
return bytes(s, encoding="utf8")
# 定義一個url和實際要執行的函數的對應關係
list1 = [
("/index/", index),
("/home/", home),
]
while 1:
# 等待鏈接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客戶端發來的消息
# 從data中取到路徑
data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
# 根據不一樣的路徑返回不一樣內容
func = None # 定義一個保存將要執行的函數名的變量
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# 返回具體的響應消息
conn.send(response)
conn.close()
返回具體的HTML文件
完美解決了不一樣URL返回不一樣內容的問題。 可是我不想僅僅返回幾個字符串,我想給瀏覽器返回完整的HTML內容,這又該怎麼辦呢?
沒問題,無論是什麼內容,最後都是轉換成字節數據發送出去的。 咱們能夠打開HTML文件,讀取出它內部的二進制數據,而後再發送給瀏覽器。
"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
返回獨立的HTML頁面
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口
sk.listen() # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url):
# 讀取index.html頁面的內容
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
# 返回字節數據
return bytes(s, encoding="utf8")
def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8")
# 定義一個url和實際要執行的函數的對應關係
list1 = [
("/index/", index),
("/home/", home),
]
while 1:
# 等待鏈接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客戶端發來的消息
# 從data中取到路徑
data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
# 根據不一樣的路徑返回不一樣內容
func = None # 定義一個保存將要執行的函數名的變量
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# 返回具體的響應消息
conn.send(response)
conn.close()
讓網頁動態起來
這網頁可以顯示出來了,可是都是靜態的啊。頁面的內容都不會變化的,我想要的是動態網站。
沒問題,我也有辦法解決。我選擇使用字符串替換來實現這個需求。(這裏使用時間戳來模擬動態的數據)
"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
返回HTML頁面
讓網頁動態起來:動態網頁的本質其實就是字符串的替換
"""
import socket
import time
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 綁定IP和端口
sk.listen() # 監聽
# 將返回不一樣的內容部分封裝成函數
def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = str(time.time())
s = s.replace("@@oo@@", now) # 在網頁中定義好特殊符號,用動態的數據去替換提早定義好的特殊符號
return bytes(s, encoding="utf8")
def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8")
# 定義一個url和實際要執行的函數的對應關係
list1 = [
("/index/", index),
("/home/", home),
]
while 1:
# 等待鏈接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客戶端發來的消息
# 從data中取到路徑
data = str(data, encoding="utf8") # 把收到的字節類型的數據轉換成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url是咱們從瀏覽器發過來的消息中分離出的訪問路徑
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 由於要遵循HTTP協議,因此回覆的消息也要加狀態行
# 根據不一樣的路徑返回不一樣內容
func = None # 定義一個保存將要執行的函數名的變量
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# 返回具體的響應消息
conn.send(response)
conn.close()
瀏覽器和服務器實現請求和響應的順序
瀏覽器(socket客戶端)
2. www.cnblogs.com(42.121.252.58,80)
sk.socket()
sk.connect((42.121.252.58,80))
sk.send('我想要xx')
5. 接收
6. 鏈接斷開
博客園(socket服務端)
1. 監聽ip和端口(42.121.252.58,80)
while True:
用戶 = 等待用戶鏈接
3. 收到'我想要xx'
4. 響應:「好」
用戶斷開
在客戶端和服務端進行完一次請求和響應後會自動斷開,當再次請求和響應的時候會從新來過,因此HTTP協議是無狀態的
WEB框架之MVC/MTV
MVC模式
MVC 是一種使用 MVC(Model View Controller 模型-視圖-控制器)設計建立 Web 應用程序的模式
Model(模型)表示應用程序核心(好比數據庫記錄列表)
View(視圖)顯示數據(數據庫記錄)
Controller(控制器)處理輸入(寫入數據庫記錄)
MVC 模式同時提供了對 HTML、CSS 和 JavaScript 的徹底控制。
Model(模型)是應用程序中用於處理應用程序數據邏輯的部分。一般模型對象負責在數據庫中存取數據。
View(視圖)是應用程序中處理數據顯示的部分。一般視圖是依據模型數據建立的前端網頁。
Controller(控制器)是應用程序中處理用戶交互的部分。一般控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送數據映射,模式渲染等。
MTV模式
MTV(Model Templates Views):
Model(模型) -- Templates(模版) --Views(視圖)
Django是標準的MTV框架。
Django處理順序
一、wsgi
二、控制器(django框架自己)
控制用戶輸入,url匹配,經過映射列表將一個請求發送到一個合適的視圖;
三、views --Views
python程序,向模型和模板發送(或獲取)數據;
四、模型綁定 --Model
五、模板引擎 --Templates
用於將內容與展示分離,描述了數據如何展示(如網頁模板);
六、模式渲染 --Views
七、控制器(django框架自己)

MVC和MTV的區別
MVC即模型-視圖-控制器模式,就是爲那些須要爲一樣的數據提供多個視圖的應用程序而設計的。它很好地實現了數據層與表示層的分離,特別適用於開發與用戶圖形界面有關的應用程序。
控制器用來處理用戶命令以及程序事件;模型維護數據並提供數據訪問方法;視圖用於數據的顯示。
MTV即模型-模版-視圖模式,其標準名稱是有爭議的。在MVC的解釋中,視圖描述了展示給用戶的數據,是指所看到的數據,而不是如何看見它。在python中視圖是指對某一特定URL的回調函數,
由於回調函數描述了所要展示的數據。模版用於將內容與展示分離。在django中,視圖描述了要展示的數據,而視圖通常轉交給模版。模版描述了數據如何展示。控制器則是指django框架自己,
經過URL配置,系統將一個請求發送到一個合適的視圖。
轉自:http://www.cnblogs.com/daliangtou/p/5258905.html
服務器程序和應用程序
對於真實開發中的python web程序來講,通常會分爲兩部分:服務器程序和應用程序。
服務器程序負責對socket服務器進行封裝,並在請求到來時,對請求的各類數據進行整理。
應用程序則負責具體的邏輯處理。
爲了方便應用程序的開發,就出現了衆多的Web框架,例如:Django、Flask、web.py 等。不一樣的框架有不一樣的開發方式,可是不管如何,開發出的應用程序都要和服務器程序配合,才能爲用戶提供服務。這樣,服務器程序就須要爲不一樣的框架提供不一樣的支持。這樣混亂的局面不管對於服務器仍是框架,都是很差的。對服務器來講,須要支持各類不一樣框架,對框架來講,只有支持它的服務器才能被開發出的應用使用。
這時候,標準化就變得尤其重要。咱們能夠設立一個標準,只要服務器程序支持這個標準,框架也支持這個標準,那麼他們就能夠配合使用。一旦標準肯定,雙方各自實現。這樣,服務器能夠支持更多支持標準的框架,框架也可使用更多支持標準的服務器:這就是WSGI以及和WSGI相關的wsgiref
wsgi
什麼是WSGI?
WSGI(Web Server Common Interface)是專門爲Python語言制定的web服務器與應用程序之間的網關接口規範,通俗的來講,只要一個服務器擁有一個實現了WSGI標準規範的模塊(
例如apache的mod_wsgi模塊),那麼任意的實現了WSGI規範的應用程序都能與它進行交互。所以,WSGI也主要分爲兩個程序部分:服務器部分和應用程序部分;經常使用的WSGI服務器有uwsgi、Gunicorn
什麼是wsgiref?
wsgiref則是官方給出的一個實現了WSGI標準用於演示用的簡單Python內置庫,它實現了一個簡單的WSGI Server和WSGI Application(在simple_server模塊中),主要分爲五個模塊:
simple_server, util, headers, handlers, validate。
wsgiref由什麼用?
咱們利用wsgiref模塊來替換咱們本身寫的web框架的socket server部分
wsgiref源碼地址:https://pypi.python.org/pypi/wsgiref
wsgiref
咱們利用wsgiref模塊來替換咱們本身寫的web框架的socket server部分:
"""
根據URL中不一樣的路徑返回不一樣的內容--函數進階版
返回HTML頁面
讓網頁動態起來
wsgiref模塊版
"""
import time
from wsgiref.simple_server import make_server
# 將返回不一樣的內容部分封裝成函數
def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = str(time.time())
s = s.replace("@@oo@@", now)
return bytes(s, encoding="utf8")
def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8")
# 定義一個url和實際要執行的函數的對應關係
list1 = [
("/index/", index),
("/home/", home),
]
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 設置HTTP響應的狀態碼和頭信息
url = environ['PATH_INFO'] # 取到用戶輸入的url
func = None
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
return [response, ]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8090, run_server)
print("我在8090等你哦...")
httpd.serve_forever()
jinja2
上面的代碼實現了一個簡單的動態,我徹底能夠從數據庫中查詢數據,而後去替換我html中的對應內容,而後再發送給瀏覽器完成渲染。 這個過程就至關於HTML模板渲染數據。 本質上就是HTML內容中利用一些特殊的符號來替換要展現的數據。 我這裏用的特殊符號是我定義的,其實模板渲染有個現成的工具: jinja2
下載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文件:
from wsgiref.simple_server import make_server
from jinja2 import Template
def index():
with open("index2.html", "r") as f:
data = f.read()
template = Template(data) # 生成模板文件
ret = template.render({"name": "Alex", "hobby_list": ["燙頭", "泡吧"]}) # 把數據填充到模板裏面
return [bytes(ret, encoding="utf8"), ]
def home():
with open("home.html", "rb") as f:
data = f.read()
return [data, ]
# 定義一個url和函數的對應關係
URL_LIST = [
("/index/", index),
("/home/", home),
]
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 設置HTTP響應的狀態碼和頭信息
url = environ['PATH_INFO'] # 取到用戶輸入的url
func = None # 將要執行的函數
for i in URL_LIST:
if i[0] == url:
func = i[1] # 去以前定義好的url列表裏找url應該執行的函數
break
if func: # 若是能找到要執行的函數
return func() # 返回函數的執行結果
else:
return [bytes("404沒有該頁面", encoding="utf8"), ]
if __name__ == '__main__':
httpd = make_server('', 8000, run_server)
print("Serving HTTP on port 8000...")
httpd.serve_forever()
如今的數據是咱們本身手寫的,那可不能夠從數據庫中查詢數據,來填充頁面呢?
使用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是什麼
Django是一個開放源代碼的Web應用框架,由Python寫成。採用了MT‘V的框架模式,即模型M,模板T和視圖V。它最初是被開發來用於管理勞倫斯出版集團旗下的一些以新聞內容爲主的網站的,
便是CMS(內容管理系統)軟件。並於2005年7月在BSD許可證下發布。這套框架是以比利時的吉普賽爵士吉他手Django Reinhardt來命名的。
Django能夠幹什麼
Django工做機制
當訪問url的時候,Django會根據ROOT_URLCONF的設置來裝載URLConf。
而後按順序逐個匹配URLConf裏的URLpatterns。若是找到則會調用相關聯的視圖函數,並把HttpRequest對象做爲第一個參數(一般是request)
最後該view函數負責返回一個HttpResponse對象。

Django的組成
Django做爲一個完善的web框架,主要包含以下幾個部分
用於進行數據持久化的ORM模塊
用於進行URL地址分配的路由模塊
用於進行模板頁面處理的模板系統
用於進行表單操做的表單模型
用於進行性能突破的緩存系統
專業術語解釋
持久化:數據永久的保存的過程稱爲數據的持久化
ORM:將程序中的對象[Object]和數據庫中的表[Relation]創建關聯關係[Mapping]的過程稱爲ORM
路由:模擬生活中的路由器,將請求URL地址和對應的函數進行關聯的操做稱爲路由
Django官網下載頁面
命令安裝指定版本(安裝最新LTS版):
cmd中輸入一下命令便可:
pip3 install django==1.11.9
Pycharm安裝Django:
點擊>File | Settings | Project: mysite | Project Interpreter |點擊最右邊的加號 |在彈出頁面的搜索框中輸入Django |在右下方勾選Specify version選中安裝版本|
點擊左下方的 Install Package便可完成
Pycharm刪除已安裝版本:
點擊>File | Settings | Project: mysite | Project Interpreter | 右邊方框中選中已經安裝好的Django | 最右邊選中紅色減號便可完成刪除
命令刪除已經安裝的版本:
建立一個django項目:
首先必須切換到項目的指定位置(將要保存項目多的地方)
執行下面的命令建立了一個名爲"mysite"的Django 項目:
django-admin startproject mysite
使用Pycharm建立
File--》New Project-->Django-->(Location項目路徑,Interpreter是所安裝的Python的版本)-->右下角點擊create便可
Django結構目錄介紹:
mysite/
├── manage.py # 管理文件
└── mysite # 項目目錄
├── __init__.py
├── settings.py # 配置
├── urls.py # 路由 --> URL和函數的對應關係
└── wsgi.py # runserver命令就使用wsgiref模塊作簡單的web server 和服務器相關的
Django結構分析
Django 程序
Django - 對整個程序進行配置
settings - 配置文件
usls - url對應關係 對應邏輯函數 爲每一個邏輯函數 分配相應視圖
wsgi - 遵循WSIG規範, uwsgi + nginx
manage.py - #管理Django 程序
執行的相關命令:
python manage.py
python manage.py startapp 名稱 建立APP
數據庫遷移:
python manage.py makemigrations
python manage.py migrate
APP
migrations 數據庫操做記錄->相應表結構發生變化,好比說字段類型,並非增刪改爲的變更
admin Django 爲咱們提供的後臺管理
apps 配置當前APP
models ORM,寫指定的類經過命令能夠建立數據庫結構.
tests 單元測試
Views 業務邏輯代碼
運行Django項目:
執行manage.py文件
python manage.py runserver 127.0.0.1:8000
模板文件配置:
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',
],
},
},
]
配置靜態文件
對於靜態文件,也就是 咱們Templates
存放html
文件視圖所對應相關的JS
,CSS
,圖片
存放的對方 一般咱們在 程序目錄下創建一個文件夾->static
(這裏寫死就能夠了,不要換別的名字)
在settings.py
文件下添加以下配置,意思是在視圖對應的靜態文件會自動在static
目錄下檢索
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',
]
Djang中Project和APP的區別
project 是 整個項目(網站)
APP是指項目裏的某個功能模塊,好比user 是一個APP
如下內容爲轉載部分
Django原理講解
url請求---->訪問路由系統(負責分發請求到相應視圖函數)------>視圖函數(處理請求)------>DataBase(數據庫操做數據生成對應頁面返回給用戶)
本質是Django就是一個Socket服務端,用戶的瀏覽器其實就是一個Socket客戶端.用戶訪問網站的過程就是服務端與客戶端Socket通訊的過程
import socket
def handle_request(client):
buf = client.recv(1024)
client.send("HTTP/1.1 200 OK\r\n\r\n".encode())
f = open('index.html', 'rb')
data = f.read()
client.send(data)
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8000))
sock.listen(5)
while True:
connection, address = sock.accept()
handle_request(connection)
connection.close()
if __name__ == '__main__':
main()
Django中請求的生命週期
概述
首先咱們知道HTTP請求及服務端響應中傳輸的全部數據都是字符串.
在Django中,當咱們訪問一個的url時,會經過路由匹配進入相應的html網頁中.
請求生命週期概念
是指當用戶在瀏覽器上輸入url到用戶看到網頁的這個時間段內,Django後臺所發生的事情
而Django的生命週期內到底發生了什麼呢??
1. 當用戶在瀏覽器中輸入url時,瀏覽器會生成請求頭和請求體發給服務端
請求頭和請求體中會包含瀏覽器的動做(action),這個動做一般爲get或者post,體如今url之中.
2. url通過Django中的wsgi,再通過Django的中間件,最後url到過路由映射表,在路由中一條一條進行匹配,
一旦其中一條匹配成功就執行對應的視圖函數,後面的路由就再也不繼續匹配了.
3. 視圖函數根據客戶端的請求查詢相應的數據.返回給Django,而後Django把客戶端想要的數據作爲一個字符串返回給客戶端.
4. 客戶端瀏覽器接收到返回的數據,通過渲染後顯示給用戶.
第一步:瀏覽器輸入網址。接下來你覺得就到django的urls了?No,緊接着是要通過django裏的settings.py裏的MIDDLEWARE配置,也就是中間件。
第二步:中間件經過以後纔會到urls,經過urls的配置,找到views裏的函數或類
第三步:執行函數或類,返回一個字符串。
第四步:再經過一系列的中間件。
第五步:前端或模板語言獲取到字符串,而後解析,在頁面上展現出來。
視圖函數根據客戶端的請求查詢相應的數據後.若是同時有多個客戶端同時發送不一樣的url到服務端請求數據
服務端查詢到數據後,怎麼知道要把哪些數據返回給哪一個客戶端呢??
所以客戶端發到服務端的url中還必需要包含所要請求的數據信息等內容.
例如,http://www.aaa.com/index/?nid=user
這個url中,
客戶端經過get請求向服務端發送的nid=user的請求,服務端能夠經過request.GET.get("nid")的方式取得nid數據
客戶端還能夠經過post的方式向服務端請求數據.
當客戶端以post的方式向服務端請求數據的時候,請求的數據包含在請求體裏,這時服務端就使用request.POST的方式取得客戶端想要取得的數據
須要注意的是
request.POST是把請求體的數據轉換一個字典,請求體中的數據默認是以字符串的形式存在的.
FBV模式和CBV模式
FBV
一個url對應一個視圖函數,這個模式叫作FBV(Function Base Views)
CBV
CBV(Class Base views),即一個url對應一個類
例子:使用cbv模式來請求網頁
路由信息:
urlpatterns = [
url(r'^fbv/',views.fbv),
url(r'^cbv/',views.CBV.as_view()),
]
視圖函數配置:
from django.views import View
class CBV(View):
def get(self,request):
return render(request, "cbv.html")
def post(self,request):
return HttpResponse("cbv.get")
cbv.html網頁的內容:
<body>
<form method="post" action="/cbv/">
{% csrf_token %}
<input type="text">
<input type="submit">
</form>
</body>
啓動項目,在瀏覽器中輸入http://127.0.0.1:8000/cbv/
,回車,獲得的網頁以下:

在input框中輸入"hello",後回車,獲得的網頁以下:

使用fbv的模式,在url匹配成功以後,會直接執行對應的視圖函數.
而若是使用cbv模式,在url匹配成功以後,會找到視圖函數中對應的類,而後這個類回到請求頭中找到對應的Request Method
.
若是是客戶端以post的方式提交請求,就執行類中的post方法;
若是是客戶端以get的方式提交請求,就執行類中的get方法
而後查找用戶發過來的url,而後在類中執行對應的方法查詢生成用戶須要的數據.
fbv方式請求的過程
用戶發送url請求,Django會依次遍歷路由映射表中的全部記錄,一旦路由映射表其中的一條匹配成功了,
就執行視圖函數中對應的函數名,這是fbv的執行流程
cbv方式請求的過程
當服務端使用cbv模式的時候,用戶發給服務端的請求包含url和method,這兩個信息都是字符串類型
服務端經過路由映射表匹配成功後會自動去找dispatch方法,而後Django會經過dispatch反射的方式找到類中對應的方法並執行
類中的方法執行完畢以後,會把客戶端想要的數據返回給dispatch方法,由dispatch方法把數據返回經客戶端
例子,把上面的例子中的視圖函數修改爲以下:
from django.views import View
class CBV(View):
def dispatch(self, request, *args, **kwargs):
print("dispatch......")
res=super(CBV,self).dispatch(request,*args,**kwargs)
return res
def get(self,request):
return render(request, "cbv.html")
def post(self,request):
return HttpResponse("cbv.get")
打印結果:
<HttpResponse status_code=200, "text/html; charset=utf-8">
dispatch......
<HttpResponse status_code=200, "text/html; charset=utf-8">
須要注意的是:
以get方式請求數據時,請求頭裏有信息,請求體裏沒有數據 以post請求數據時,請求頭和請求體裏都有數據.
Django登陸實現
- 在本身創建的APP 裏的
Views
中添加邏輯函數 login
render
這個模塊至關於一個 open
讀取html
文件中的數據返回給瀏覽器
from django.shortcuts import render,redirect
def login(request):
# request 包含用戶提交的全部信息
error_msg = ""
if request.method == 'POST':
# 獲取用戶經過post 提交過來的數據
user = request.POST.get('user',None)
pwd = request.POST.get('pwd',None)
if user == 'admin' and pwd == "admin":
#跳轉到響應頁面 ("/"至關於前面的地址127.1.0.0:8000)
return redirect('/home')
else:
#用戶名密碼不匹配
error_msg = "錯了 !!!!天啊!!"
//error_msg 替換html文件 相同字段文字
return render(request, 'login.html',{'error_msg':error_msg})
2.登陸函數邏輯所對應的 templates
文件中的login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
<style>
label{
width: 80px;
text-align: right;
display: inline-block;
}
</style>
</head>
<body>
<form action="/login/" method="post" style="background-color: cadetblue">
<p>
<label for="username">用戶名</label>
<input id="username" name="user" type="text"/>
</p>
<p>
<label for="password">密碼</label>
<input id="password" name="pwd" type="text"/>
<input type="submit" value="提交">
//error_msg 替換請求 相同字段文字
<span style="color: red">{{ error_msg}}</span>
</p>
</form>
</body>
</html>
頁面數據的操做功能
一樣的在本身創建的APP 裏的Views
中添加邏輯函數 home
USER_LIST = []
#
# for index in range(20):
# tem = {'username':'雪芙'+str(index),'sex':'女','age':20+index}
# USER_LIST.append(tem)
def home(request):
if request.method == 'POST':
#獲取用戶提交的數據 POST 請求中
u = request.POST.get('username')
s = request.POST.get('sex')
a = request.POST.get('age')
tem = {'username': u, 'sex': s, 'age':a}
USER_LIST.append(tem)
//當首次沒有數據時候,列表爲空
return render(request,'home.html',{'user_list':USER_LIST})
- home函數邏輯所對應的
templates
文件中的home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home</title>
</head>
<body style="margin: 0">
<div style="height:48px;background-color: cadetblue"></div>
<div>
<form action="/home/" method="post">
<input type="text" name="username" placeholder="用戶名"/>
<input type="text" name="sex" placeholder="性別"/>
<input type="text" name="age" placeholder="年齡"/>
<input type="submit" value="添加">
</form>
</div>
<div>
<table>
//頁面中的for循環寫法
{% for row in user_list %}
<tr>
<td>{{ row.username }}</td>
<td>{{ row.sex }}</td>
<td>{{ row.age }}</td>
</tr>
{% endfor %}
</table>
</div>
</body>
</html>
總結
Django的主要目的是簡便、快速的開發數據庫驅動的網站,Django有許多功能強大的第三方,本質是上是一個服務端的Socket鏈接,可是功能及其強大,封裝後的功能簡單易操做,深受廣大用戶喜好
.配合上模板頁面,數據處理,就能夠創建本身的網站或者移動應用程序.
Django基礎必備三件套:
from django.shortcuts import HttpResponse, render, redirect
HttpResponse
內部傳入一個字符串參數,返回給瀏覽器。
def index(request):
# 業務邏輯代碼
return HttpResponse("OK")
render
除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數。
將數據填充進模板文件,最後把結果返回給瀏覽器。(相似於咱們上面用到的jinja2)
def index(request):
# 業務邏輯代碼
return render(request, "index.html", {"name": "alex", "hobby": ["燙頭", "泡吧"]})
redirect
接受一個URL參數,表示跳轉到指定的URL。
def index(request):
# 業務邏輯代碼
return redirect("/home/")
Django基礎注意事項
1:get和post的應用場景
訪問網站的時候用get向網頁提交數據的時候用post
2: 由頁面向後端提交數據的時候,使用FORM表單
3:使用form表單注意事項
form表單中必需要寫action屬性和method屬性,若是涉及到提交文件的話必須寫enctype屬性
4: input標籤必須放在form表單中,input標籤必須寫name屬性
5: 提交按鈕類型是submit(input type=' submit')
啓動Django報錯:
Django 啓動時報錯 UnicodeEncodeError ...
報這個錯誤一般是由於計算機名爲中文,改爲英文的計算機名重啓下電腦就能夠了。
Django運行常見錯誤
詳情連接