第十二章 Django框架

第十二章 Django框架

  • tcp/ip五層模型css

    • 應用層html

    • 傳輸層前端

    • 網絡層python

    • 數據鏈路層mysql

    • 物理層jquery

  • socket : 套接字,位於應用層和傳輸層之間的虛擬層,是一組接口git

  • c/s架構 ------> b/s架構web

    #百度瀏覽器 socket服務器
    - 1.建立socket服務端
    - 2.綁定ip和接口
    - 3.監聽
    - 4.等待鏈接
    - 7.接受數據
    - 8.返回數據
    #瀏覽器 socket客戶端
    - 5.鏈接服務端
    - 6.發送數據
    - 9.接受數據
    - 10.斷開鏈接

12.1web框架

12.1.1web框架本質

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

  • socket服務端正則表達式

    import socket
    #建立一個socket對象
    sk = socket.socket()
    #綁定一個ip和端口
    sk.bind(('127.0.0.1',8000))
    #監聽
    sk.listen()
    #等待鏈接
    while True:
       conn,addr = sk.accept()
       #接受數據
       conn.recv(1024)
       #返回數據
       conn.send(b'ok')
       #斷開鏈接
       conn.close()
  • 在瀏覽器中鏈接上述服務端時出現問題:

咱們能夠一下能夠發現瀏覽器向服務端發送請求時,沒法響應,那麼是什麼致使這個緣由呢?咱們想到必須有一個統一的規則,讓你們發送消息、接收消息的時候都有個格式依據,不能隨便寫。這個規則就是HTTP協議.HTTP協議主要規定了客戶端和服務器之間的通訊格式

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

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()  

輸出

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替換成換行看得更清晰點:

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

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

12.1.2HTTP協議

HTTP協議,又稱超文本傳輸協議(英文:Hyper Text Transfer Protocol,HTTP)是一種用於分佈式、協做式和超媒體信息系統的應用層協議,

每一個HTTP請求和響應都遵循相同的格式,一個HTTP包含Header和Body兩部分,其中Body是可選的。HTTP響應的Header中有一個 Content-Type代表響應的內容格式。它的值如text/html; charset=utf-8。text/html則表示是網頁,charset=utf-8則表示編碼爲utf-8。

12.1.2.1HTTP工做原理

  • HTTP協議採用了請求/響應模型。客戶端向服務器發送一個請求報文,請求報文包含請求的方法、URL、協議版本、請求頭部和請求數據。服務器以一個狀態行做爲響應,響應的內容包括協議的版本、成功或者錯誤代碼、服務器信息、響應頭部和響應數據。

  • HTTP 請求/響應的步驟:

    • 1.客戶端鏈接到Web服務器

    • 2.發送HTTP請求

    • 3.服務器接受請求並返回HTTP響應

    • 4.釋放鏈接TCP鏈接

    • 5.客戶端瀏覽器解析HTML內容

  • 在瀏覽器地址欄鍵入URL,按下回車以後會經歷如下流程:

    • 1.瀏覽器向 DNS 服務器請求解析該 URL 中的域名所對應的 IP 地址;

    • 2.解析出 IP 地址後,根據該 IP 地址和默認端口 80,和服務器創建TCP鏈接;

    • 3.瀏覽器發出讀取文件(URL 中域名後面部分對應的文件)的HTTP 請求,該請求報文做爲 TCP 三次握手的第三個報文的數據發送給服務器;

    • 4.服務器對瀏覽器請求做出響應,並把對應的 html 文本發送給瀏覽器;

    • 5.釋放 TCP鏈接;

    • 6.瀏覽器解析將該 html 文本並顯示內容;

12.1.2.2http請求方法

1.GET

  • 向指定的資源發出「顯示」請求。使用GET方法應該只用在讀取數據,而不該當被用於產生「反作用」的操做中,例如在Web Application中。其中一個緣由是GET可能會被網絡蜘蛛等隨意訪問。

2.POST

  • 向指定資源提交數據,請求服務器進行處理(例如提交表單或者上傳文件)。數據被包含在請求報文中。這個請求可能會建立新的資源或修改現有資源,或兩者皆有。

3.HEAD

  • 與GET方法同樣,都是向服務器發出指定資源的請求。只不過服務器將不傳回資源的本文部分。它的好處在於,使用這個方法能夠在沒必要傳輸所有內容的狀況下,就能夠獲取其中「關於該資源的信息」(元信息或稱元數據)。

4.PUT

  • 向指定資源位置上傳其最新內容。

5.DELETE

  • 請求服務器刪除Request-URI所標識的資源。

6.TRACE

  • 回顯服務器收到的請求,主要用於測試或診斷。

7.OPTIONS

  • 這個方法可以使服務器傳回該資源所支持的全部HTTP請求方法。用'*'來代替資源名稱,向Web服務器發送OPTIONS請求,能夠測試服務器功能是否正常運做。

8.CONNECT

  • HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。一般用於SSL加密服務器的連接(經由非加密的HTTP代理服務器)。

注意:

  • 1.方法名稱是區分大小寫,當某個請求所對應的資源部支持對應方法時,服務器應當返回狀態碼405(Method Not Allowed),當服務器不認識或者不支持對應的請求方法的時候,應當返回狀態碼501(Not Implemented)。

  • 2.HTTP服務器至少應該實現GET和HEAD方法,其餘方法都是可選的。固然,全部的方法支持的實現都應當匹配下述的方法各自的語義定義。此外,除了上述方法,特定的HTTP服務器還可以擴展自定義的方法。例如PATCH(由 RFC 5789 指定的方法)用於將局部修改應用到資源

12.1.2.3http狀態碼

全部HTTP響應的第一行都是狀態行,依次是當前HTTP版本號,3位數字組成的狀態代碼,以及描述狀態的短語,彼此由空格分隔。

狀態代碼的第一個數字表明當前響應的類型:

  • 1xx消息——請求已被服務器接收,繼續處理

  • 2xx成功——請求已成功被服務器接收、理解、並接受

  • 3xx重定向——須要後續操做才能完成這一請求

  • 4xx請求錯誤——請求含有詞法錯誤或者沒法被執行

  • 5xx服務器錯誤——服務器在處理某個正確請求時發生錯誤

12.1.2.4資源定位符url

超文本傳輸協議(HTTP)的統一資源定位符將從因特網獲取信息的五個基本元素包括在一個簡單的地址中:

  • 傳送協議。

  • 層級URL標記符號(爲[//],固定不變)

  • 訪問資源須要的憑證信息(可省略)

  • 服務器(一般爲域名,有時爲IP地址)

  • 端口號(以數字方式表示,若爲HTTP的默認值「:80」可省略)

  • 路徑(以「/」字符區別路徑中的每個目錄名稱)

  • 查詢(GET模式的窗體參數,以「?」字符爲起點,每一個參數以「&」隔開,再以「=」分開參數名稱與數據,一般以UTF8的URL編碼,避開字符衝突的問題)

  • 片斷。以「#」字符爲起點

http://www.luffycity.com:80/news/index.html?id=250&page=1 爲例:

http默認端口80,https默認端口443

12.1.2.5HTTP請求格式

#請求(request)
#瀏覽器 —— 》 服務器  
############   GET請求 沒有請求數據
「請求方式 url路徑 協議版本\r\n
k1:v1\r\n
k2:v2\r\n
\r\n
數據」

12.1.2.6HTTP響應格式

#響應(response)
#服務器 —— 》 瀏覽器
「協議版本 狀態碼 狀態碼描述\r\n
k1:v1\r\n
k2:v2\r\n
\r\n
響應數據(響應體)」

12.1.3web框架的功能

  • 1.socket收發消息 - wsgiref wsgiref (本地測試) uwsgi(線上)

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

  • 3.返回動態頁面(字符串的替換------->模板的渲染) —— jinja2

django flask tornado
2 3 2 1 2 3

12.1.4自定義web框架

socket服務端

import socket

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

while True:
   conn, addr = sk.accept()
   data = conn.recv(8096)
   print(data)  # 將瀏覽器發來的消息打印出來
   conn.send(b"HTTP/1.1 200 0k\r\n\r\n<h1>OK</h1>")

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

import socket  
  
sk = socket.socket()  
sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口  
sk.listen()  # 監聽  
  
while True:  
    # 等待鏈接  
    conn, add = sk.accept()  
    data = conn.recv(8096)  # 接收客戶端發來的消息  
    # 從data中取到路徑  
    data = str(data,encoding="utf-8")  # 把收到的字節類型的數據轉換成字符串  
    # 按\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()  

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

import socket

# 建立一個socket對象
sk = socket.socket()
# 綁定IP和端口
sk.bind(('127.0.0.1', 8000))
# 監聽
sk.listen(5)

# 等待鏈接
def index(url):
    ret = '<h1>index!</h1>({})'.format(url)
    return ret.encode('utf-8')

def home(url):
    ret = '<h1>home!</h1>({})'.format(url)
    return ret.encode('utf-8')

list1 = [
    ('/index', index),
    ('/home', home),
]
while True:
    conn, addr = sk.accept()
    # 接收數據
    data = conn.recv(1024)
    data = data.decode('utf-8')
    url = data.split()[1]
    #先發送報頭
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    func = None
    for i in list1:
        if url == i[0]:
            func = i[1]
            break
    if func:
        ret = func(url)
    else:
        ret = b'<h1>404 not found!</h1>'
    conn.send(ret)
    # 斷開鏈接
    conn.close()

返回具體的HTML文件

import socket  
  
sk = socket.socket()  
sk.bind(("127.0.0.1", 8080))  # 綁定IP和端口  
sk.listen()  # 監聽  
  
  
# 將返回不一樣的內容部分封裝成不一樣的函數  
def index(url):    #建立一個html文件,內容隨意
    # 讀取index.html頁面的內容  
    with open("index.html", "r", encoding="utf8") as f:  
        s = f.read()  
    # 返回字節數據  
    return bytes(s, encoding="utf8")  
  
  
def home(url):    #建立一個html文件,內容隨意
    with open("home.html", "r", encoding="utf8") as f:  
        s = f.read()  
    return bytes(s, encoding="utf8")  
  
  
# 定義一個url和實際要執行的函數的對應關係  
list1 = [  
    ("/index/", index),  
    ("/home/", home),  
]  
  
while True:  
    # 等待鏈接  
    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 item in list1:  
        if item[0] == url:  
            func = item[1]  
            break  
    if func:  
        response = func(url)  
    else:  
        response = b"404 not found!"  
  
    # 返回具體的響應消息  
    conn.send(response)  
    conn.close()  

讓網頁動起來

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")  
 
 
def timer(url):  
   import time  
   with open("time.html", "r", encoding="utf8") as f:  
       s = f.read()  
       s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S"))  
   return bytes(s, encoding="utf8")  
 
 
# 定義一個url和實際要執行的函數的對應關係  
list1 = [  
  ("/index/", index),  
  ("/home/", home),  
  ("/time/", timer),  
]  
 
while True:  
   # 等待鏈接  
   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 item in list1:  
       if item[0] == url:  
           func = item[1]  
           break  
   if func:  
       response = func(url)  
   else:  
       response = b"404 not found!"  
 
   # 返回具體的響應消息  
   conn.send(response)  
   conn.close()  

11.2.5服務器程序和應用程序

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開發環境用的就是這個模塊來作服務器。

11.2.5.1wsgiref

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

"""  
根據URL中不一樣的路徑返回不一樣的內容--函數進階版  
返回HTML頁面  
讓網頁動態起來  
wsgiref模塊版  
"""   
     
from wsgiref.simple_server import make_server   
     
     
# 將返回不一樣的內容部分封裝成函數   
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")   
     
     
def timer(url):   
    import time   
    with open("time.html", "r", encoding="utf8") as f:   
        s = f.read()   
        s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S"))   
    return bytes(s, encoding="utf8")   
     
     
# 定義一個url和實際要執行的函數的對應關係   
list1 = [   
    ("/index", index),   
    ("/home", home),   
    ("/time", timer),   
]   
     
     
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()  

11.2.5.2jinja2

index2.html文件

<!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>

使用jinja2渲染index2.html文件:

from
wsgiref.simple_server import make_server  
from jinja2 import Template  
  
  
def index(url):  
    # 讀取HTML文件內容  
    with open("index2.html", "r", encoding="utf8") as f:  
        data = f.read()  
        template = Template(data)   # 生成模板文件  
        ret = template.render({'name': 'alex', 'hobby_list': ['抽菸', '喝酒', '燙頭']})   # 把數據填充到模板中  
    return bytes(ret, 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()  

使用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()

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

12.2Django

12.2.1 Django框架

12.2.1.1MVC框架和MTV框架

  • MVC,全名是Model View Controller,是軟件工程中的一種軟件架構模式,把軟件系統分爲三個基本部分:模型(Model)、視圖(View)和控制器(Controller),具備耦合性低、重用性高、生命週期成本低等優勢。

    深刻了解:http://www.ruanyifeng.com/blog/2007/11/mvc.html

     

     

  • Django框架的不一樣之處在於它拆分的三部分爲:Model(模型)、Template(模板)和View(視圖),也就是MTV框架。

12.2.1.2Django的MTV模式

  • Model(模型):負責業務對象與數據庫的對象(ORM)

    ORM:對象關係映射

  • Template(模版):負責如何把頁面展現給用戶

  • View(視圖):負責業務邏輯,並在適當的時候調用Model和Template

  • 此外,Django還有一個urls分發器,它的做用是將一個個URL的頁面請求分發給不一樣的view處理,view再調用相應的Model和Template

12.2.2基礎命令

12.2.2.1下載安裝

  • cmd命令行

    • pip install django==1.11.21

    • pip install django==1.11.21 -i 國內源

  • pycharm

    • file--->setting--->

12.2.2.2建立項目

  • 命令行

    • 建立一個文件夾用於存放項目文件,

    • shift + 鼠標右鍵 ----->點擊"在此處打開命令窗口(w)"

    • django-admin startproject 項目名稱

      項目目錄

  • pycharm

12.2.2.3啓動項目

  • 命令行

    • cd 項目根目錄 ---------> manage.py

      python36 manage.py runserver (默認是80端口)

      python36 manage.py runserver 90 在80端口啓動

      python36 manage.py runserver 0.0.0.0:80` 0.0.0.0:80

      若是運行不成功,嘗試進行下面操做,而後重啓

  • pycharm

    更改端口和ip

12.2.2.4建立app

  • 命令行

    • python36 manage.py startapp app01

  • pacharm工具建立

    • tools ——》 run manage.py task ——》 startapp app名稱

  • 第三種建立方式

註冊app

INSTALLED_APPS = [
	...
    'app01',
    'app01.apps.App01Config'  #推薦寫法
]

將urls裏的兩個函數封裝到views.py中

而後再urls導入views.py

12.2.2.5數據庫遷移

python manage.py makemigrations
python manage.py migrate

12.2.2.6建立超級用戶

python manage.py createsuperuser

輸入以上命令後,根據提示輸入用戶名、郵箱、密碼、確認密碼。密碼的要求至少是不八位,不能和郵箱太接近,兩次密碼須要一致。

  • 簡單使用

    模板 ——》HTML 指定文件夾

    urls.py

    # 導入
    from django.shortcuts import HttpResponse,render
    
    # 函數
    def index(request):
        # return HttpResponse('index')
        return render(request,'index.html')
    
    # url和函數對應關係
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', index),
    ]
    

12.2.3Django基礎相關

12.2.3.1Django必備三件套

from django.shortcuts import HttpResponse, render, redirect
  • HttpResponse

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

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

    • 除request參數外還接受一個待渲染的模板文件和一個保存具體數據的字典參數。將數據填充進模板文件,最後把結果返回給瀏覽器。

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

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

      def index(request):
          # 業務邏輯代碼
          return redirect("https://www.baidu.com")
      

    重定向

  • 啓動Django報錯

    Django 啓動時報錯 UnicodeEncodeError ...

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

12.2.3.2Django中使用MySQL數據庫的流程

  • 1.建立一個MySQL數據庫。

  • 2.在settings中配置,Django鏈接MySQL數據庫:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',    # 引擎	
            'NAME': 'day53',						# 數據庫名稱
            'HOST': '127.0.0.1',					# ip地址
            'PORT':3306,							# 端口
            'USER':'root',							# 用戶
            'PASSWORD':'123'						# 密碼,沒有時寫成''
            
        }
    }
    

  • 3.在與settings同級目錄下的 init文件中寫:

    import pymysql
    pymysql.install_as_MySQLdb()
    

  • 4.建立表結構(在app下的models.py中寫類)

    from django.db import models
    
    class User(models.Model):
        username = models.CharField(max_lengh=32)   #username  varchar
        password = models.CharField(max_lengh=32)  # password  varchar
    

  • 5.執行數據庫遷移的命令

    python manage.py makemigrations   #  檢測每一個註冊app下的model.py   記錄model的變動記錄
    
    python manage.py migrate   #  同步變動記錄到數據庫中
    

    建立數據

    提交數據

1.orm操做

# 獲取表中全部的數據
ret = models.User.objects.all()  # QuerySet 對象列表  【對象】
# 獲取一個對象(有且惟一)
obj = models.User.objects.get(username='alex')   # 獲取不到或者獲取到多個對象會報錯
# 獲取知足條件的對象
ret = models.User.objects.filter(username='wusir',password='dasb')  # QuerySet 對象列表

2.GET和 POST區別

  • 從標準上來看

    • GET 用於獲取信息,是無反作用的,是冪等的,且可緩存

    • POST 用於修改服務器上的數據,有反作用,非冪等,不可緩存

  • 從請求報文看

    • GET 和 POST 只是 HTTP 協議中兩種請求方式(殊途同歸),而 HTTP 協議是基於 TCP/IP 的應用層協議,不管 GET 仍是 POST,用的都是同一個傳輸層協議,因此在傳輸上,沒有區別。

    • 報文格式上

      • 不帶參數時,最大區別就是第一行方法名不一樣, 僅僅是報文的幾個字符不一樣而已,POST 方法請求報文第一行是這樣的 POST /url HTTP/1.1 GET 方法請求報文第一行是這樣的 GET /url HTTP/1.1

      • 帶參數時,在約定中,GET 方法的參數應該放在 url 中,POST 方法參數應該放在 body 中

  • GET產生一個TCP數據包;POST產生兩個TCP數據包。

     

12.2.4Django模板

官方文檔連接:https://docs.djangoproject.com/en/1.11/ref/templates/language/

Django模板中只須要記兩種特殊符號:{{ }}表示變量,在模板渲染的時候替換成值,{% %}表示標籤,tag,表示邏輯相關的操做。

12.2.4.1經常使用語法

1.變量
  • {{ 變量名 }}

  • 變量名由字母數字和下劃線組成。

  • 點(.)在模板語言中有特殊的含義,用來獲取對象的相應屬性值。

    例:view中代碼

    from django.shortcuts import render
    
    def template_text(request):
        string = 'dasb'
        age = 73
        hobby = ['抽菸','喝酒','燙頭']
        dic = {'name':'dalao','age':age,'hobby':hobby}
        return render(request,"template_test.html",{'string':string,'age':age,'hobby':hobby,'dic':dic})
    

    模板中支持的寫法:

    {# 取l中的第一個參數 #}
    {{ l.0 }}
    {# 取字典中key的值 #}
    {{ d.name }}
    {# 取對象的name屬性 #}
    {{ person_list.0.name }}
    {# .操做只能調用不帶參數的方法 #}
    {{ person_list.0.dream }}
    

    注:當模板系統遇到一個(.)時,會按照以下的順序去查詢:

    • 1.在字典中查詢

    • 2.屬性或者方法

    • 3.數字索引

2.Filter
  • 過濾器,用來修改變量的顯示結果。

  • 語法 : {{ value|filter_name:參數}}

  • ':'左右沒有空格

  • default

    • {{ value|default:"nothing"}}

    • 若是變量value不存在或者變量爲空的話就顯示nothing

    • 注:TEMPLATES的OPTIONS能夠增長一個選項:string_if_invalid:"變量不存在",能夠替代default的的做用。

1.filesizeformat

  • 文件大小格式化

  • {{ value|filesizeformat }}

  • 範圍 : byte ~ PB

2.add

  • {{ value|add:"2" }}

  • {{2|add:4}}               # 6
    {{"2"|add:4}}             # 6
    {{"2"|add:"4"}}           # 6
    {{"2"|add:"asdf"}}        #2asdf
    {{ first|add:second }}   
    #若是first是 [1,.2,3] ,second是 [4,5,6] ,那輸出結果是 [1,2,3,4,5,6] 
    #若是first是 ["抽菸",'喝酒',"燙頭"] ,second是 ["唱","跳","rap",'籃球'] ,那輸出結果是 ["抽菸",'喝酒',"燙頭","唱","跳","rap",'籃球'] 
    
    #Django模版加法:
    {{ value|add:10}} value=5,則返回15
    #Django模版減法:
    {{value|add:-10}} value=5,則返回-5,這個比較好理解,減法就是加一個負數 
    #Django模版乘法和除法:
    {%  widthratio 5 1 100 %} 上面的代碼表示:5/1 *100,返回500,widthratio須要三個參數,它會使用 參數1/參數2*參數3,因此要進行乘法的話,就將參數2=1便可 
    
    #數據保留兩位小數
     <td>{{ foo.product_amount|floatformat:5 }}</td>
     register = template.Library()
    #一些複雜一些的運算
    利用 add 這個filter ,能夠作更瘋狂的事:
    計算 A^2: {% widthratio A 1 A %}
    計算 (A+B)^2: {% widthratio A|add:B 1 A|add:B %}
    計算 (A+B) * (C+D): {% widthratio A|add:B 1 C|add:D %}
    #除法並保留小數
    首先定義方法在templatehelper.py文件中
    @register.filter
    def div(value, div):
        '''
        分轉化爲元,保留兩位小數
        :param value:
        :param div:
        :return:
        '''
        return round((value / div), 2)
    而後在模板中能夠按照以下使用,固然前提是{% load templatehelper %}:
    <td>{{ foo.product_amount |div:100 }}</td>
    

3.lower

  • 全小寫

  • {{value|lower}}

4.upper

  • 全大寫

  • {{value|upper}}

5.title

  • 標題,首字母大寫

  • {{value|title}}

6.length

  • 計算長度

  • {{value|length}}

7.slice

  • 切片

    hobby = ["抽菸",'喝酒',"燙頭","唱","跳","rap",'籃球'] 
    
    {{hobby|slice:'-2:0:-1'}}    #["rap","跳","唱","燙頭",'喝酒']
    

8.first和last

9.join

  • 使用字符串拼接列表。同python的str.join(list)。

  • {{ value|join:" // " }}

10.truncatechars

  • 若是字符串字符多於指定的字符數量,那麼會被截斷。截斷的字符串將以可翻譯的省略號序列(「...」)結尾。

  • 參數:截斷的字符數 {{ value|truncatechars:9}}

    long_str = "you have 13 years to go to anthers"
    
    {{long_str|truncatechars:10}}   #  you hav...
    {{long_str|truncatechars:3}}    #  ...
    {{long_str|truncatewords:3}}    #  you have 13 years...
    
    long_str = "咱也不知道,咱也不敢問"
    {{long_str|truncatewords:3}}    #咱也不知道,咱也不敢問   (不識別中文)
    {{long_str|truncatechars:3}}    # ...
    {{long_str|truncatechars:4}}    # 咱...
    

11.date

12.safe

  • Django的模板中會對HTML標籤和JS等語法標籤進行自動轉義,緣由顯而易見,這樣是爲了安全。可是有的時候咱們可能不但願這些HTML元素被轉義,好比咱們作一個內容管理系統,後臺添加的文章中是通過修飾的,這些修飾多是經過一個相似於FCKeditor編輯加註了HTML修飾符的文本,若是自動轉義的話顯示的就是保護HTML標籤的源文件。爲了在Django中關閉HTML的自動轉義有兩種方式,若是是一個單獨的變量咱們能夠經過過濾器「|safe」的方式告訴Django這段代碼是安全的沒必要轉義。

    value = "<a href='#'>點我</a>"
    {{ value|safe}}
    
3.自定義Filter
  • 1.在app01下建立一個名爲templatetags的文件包(必須爲此名字)

  • 2.在該python包建立py文件,文件名自定義(my_tags.py);

  • 3..在py文件中寫:

    from django import template
    register = template.Library()   # register也不能變
    

  • 4.寫函數+裝飾器

    @register.filter
    def add_xx(value, arg):  # 最多有兩個
    
        return '{}-{}'.format(value, arg)
    
  • 5.使用:

    {% load my_tags %}
    {{ 'alex'|add_xx:'dsb' }}
    

    錯誤

    解決: 一:重啓項目

4Tags

1.for

<ul>
  {% for hobby in hobby_list %}
    <li>
        {{ forloop.counter }}.{{ hobby }}
    </li>
{% endfor %}
</ul>

  • for循環可用的一些參數:

    Variable Description
    forloop.counter 當前循環的索引值(從1開始)
    forloop.counter0 當前循環的索引值(從0開始)
    forloop.revcounter 當前循環的倒序索引值(到1結束)
    forloop.revcounter0 當前循環的倒序索引值(到0結束)
    forloop.first 當前循環是否是第一次循環(布爾值)
    forloop.last 當前循環是否是最後一次循環(布爾值)
    forloop.parentloop 本層循環的外層循環

    偶數列爲紅色

    <table border="1">
       <tbody>
           {% for hobby_list1 in hobby_list2 %}
               <tr>
                   {% for hobby in hobby_list1 %}
                       {% if forloop.counter|divisibleby:2 %}
                           <td style="color: red;">{{ hobby }}</td>
                       {% else %}
                            <td >{{ hobby }}</td>
                       {% endif %}
    
                   {% endfor %}
               </tr>
           {% endfor %}
       </tbody>
    </table>
    

2.for....empty

<ul>
{% for user in user_list %}
    <li>{{ user.name }}</li>
{% empty %}
    <li>空空如也</li>
{% endfor %}
</ul>

3.if

  • if,elif和else

  • if,else

  • if語句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判斷。

    {% if age < 73 %}
        還沒到73歲
    {% elif age == 73 %}
        恰好73歲
    {% elif age > 73 %}
        過了73歲
    {% endif %}
    

    注意 :

    • python中 :10>5>1 =》 10>5 and 5>1 true

    • js :10>5>1 =》 10>5 =》 true =》 1>1 false

    • 模板中 不支持連續連續判斷 也不支持算數運算(可用過濾器判斷)

4.with

#寫法一:只在with裏生效
{% with hobby_list2.2.3 as hobby  %}
    {{ hobby }}
    {{ hobby }}
    {{ hobby }}
{% endwith %}
<br>
#寫法二:只在with裏生效
{% with hobby=hobby_list2.2.3  %}
    {{ hobby }}
    {{ hobby }}
    {{ hobby }}
{% endwith %}

5.註釋
<!--{{'不顯示'}}-->

12.2.4.2母版和繼承

1.母版

  • 就是一個普通HTML提取多個頁面的公共部分 定義block塊

  • 繼承母版和塊(block)

    • {% extends ‘base.html’ %}

    • 重寫block塊 —— 寫子頁面獨特的內容

#注意:
1.{% extends 'base.html' %} 寫在第一行   前面不要有內容 有內容會顯示
2.把要顯示的內容寫在block塊中
3.{% extends 'base.html' %} 'base.html' 須要加上引號  否則當作變量去查找
4.定義多個block塊,定義 css  js 塊

12.2.4.3組件

能夠將經常使用的頁面內容如導航條,頁尾信息等組件保存在單獨的文件中,而後在須要使用的地方按以下語法導入便可。(例: "nav.html")

{% include 'navbar.html' %}

12.2.4.4靜態文件相關

{% load static %}  
/*{% load staticfiles %} */

 <link rel="stylesheet" href="{% static '/plugins/bootstrap-3.3.7/css/bootstrap.css' %}">
 <link rel="stylesheet" href="{% static '/css/dsb.css' %}">
/*另外一種寫法*/
<link rel="stylesheet" href="{% get_static_prefix %}/css/dsb.css">

{% static '/plugins/bootstrap-3.3.7/css/bootstrap.css' %}  
{% get_static_prefix %}   ——》 獲取別名
1.自定義simple_tag
  • 和自定義filter相似,只不過接收更靈活的參數。

    @register.simple_tag
    def join_str(*args, **kwargs):
        return '{} - {} '.format('*'.join(args), '$'.join(kwargs.values()))
    

    #使用
    {% load my_tags %}
    {% join_str '1' '2' k1='3' k2='4' %}
    

2.inclusion_tag
  • 返回一個html代碼段

3.靜態文件的配置

在settings.py寫入下面代碼

STATIC_URL = '/static/'   # 別名 ,HTML中使用的靜態文件夾前綴
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static1'),   ## 靜態文件存放位置,按照STATICFILES_DIRS列表的順序進行查找。
    os.path.join(BASE_DIR, 'static'),
    os.path.join(BASE_DIR, 'static2'),
]

<link rel="stylesheet" href="/static/css/login.css">   # 別名開頭

12.2.5Django的View(視圖)

  • 一個視圖函數(類),簡稱視圖,是一個簡單的Python 函數(類),它接受Web請求而且返回Web響應。響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片。

  • 不管視圖自己包含什麼邏輯,都要返回響應。代碼寫在哪裏也無所謂,只要它在你當前項目目錄下面。

  • 爲了將代碼放在某處,你們約定成俗將視圖放置在項目(project)或應用程序(app)目錄中的名爲views.py的文件中。

12.2.5.1CBV和FBV

  • FBV function based view (基於函數的視圖)

  • CBV class based biew (基於類的視圖)

FBV寫法:

from django.shortcuts import HttpResponse,render,redirect
#新增出版社
def add_publisher(request):
    #對請求方式進行判斷
    error = ''
    if request.method == 'POST':
        #處理POST請求
        #獲取出版社名稱
        publisher_name = request.POST.get('publisher_name')
        #判斷出版社名稱是否重複
        if models.publisher.objects.filter(name=publisher_name):
            error = "出版社名稱已存在"
        #判斷輸入值是否爲空
        if not publisher_name:
            error = "輸入不能爲空"
        if not error:
            #使用orm將數據插入到數據庫中
            obj = models.publisher.objects.create(name=publisher_name)
            print(obj.pk,obj.name)
            #跳轉到展現出版社界面
            return redirect('/publisher_list/')
    #返回一個包含form表單的頁面
    return render(request,'add_publisher.html',{'error':error})

CBV 寫法

from django.shortcuts import HttpResponse,render,redirect
#類方法寫新增出版社
from django.views import View
class Addpublisher(View):
    def get(self,request):
        #處理get請求
        return render(request,'add_publisher.html')

    def post(self,request):
        # 處理post請求
        # 處理POST請求
        error = ''
        # 獲取出版社名稱
        publisher_name = request.POST.get('publisher_name')
        # 判斷出版社名稱是否重複
        if models.publisher.objects.filter(name=publisher_name):
            error = "出版社名稱已存在"
        # 判斷輸入值是否爲空
        if not publisher_name:
            error = "輸入不能爲空"
        if not error:
            # 使用orm將數據插入到數據庫中
            obj = models.publisher.objects.create(name=publisher_name)
            print(obj.pk, obj.name)
            # 跳轉到展現出版社界面
            return redirect('/publisher_list/')
        return render(request,'add_publisher.html',{'error':error})
#路徑修改成
url(r'^add_publisher/', views.Addpublisher.as_view()),

  • as_view的流程

    • 1.項目啓動,加載urls.py時,執行 類.as_view() ,獲得了一個view函數

    • 2.請求到來時執行view函數

      • 1.實例化類------->對象 selef.request = request

      • 2.執行self.dispatch(request, *args, **kwargs)

        • 判斷請求方式是否被容許

          • 容許 :經過反射獲取到對應請求方式的方法,賦值給handler

          • 不容許:將self.http_method_not_allowed賦值給handler

        • 執行handler(request,*args,**kwargs),返回結果給jango,再給瀏覽器

12.2.5.2視圖加裝飾器

  • FBV : 直接加裝飾器

    from django.shortcuts import HttpResponse,render,redirect
    from app01 import models
    
    import time
    def timer(func):
        def inner(*args,**kwargs):
            start = time.time()
            ret = func(*args,**kwargs)
            print('函數執行時間爲{}'.format(time.time()-start))
            return ret
        return inner
    
    #展現出版社
    @timer
    def publisher_list(request):
        #從數據庫中查詢出版社信息
        all_publishers = models.publisher.objects.all().order_by("pk")
        #返回一個包含出版社信息的頁面
        return render(request,'publisher_list.html',{'all_publishers':all_publishers})
    

  • CBV :

    from django.shortcuts import HttpResponse,render,redirect
    from app01 import models
    from django.utils.decorators import method_decorator
    
    import time
    def timer(func):
        def inner(*args,**kwargs):
            start = time.time()
            ret = func(*args,**kwargs)
            print('函數執行時間爲{}'.format(time.time()-start))
            return ret
        return inner
    
    • 加在方法上

      class Addpublisher(View):
          @method_decorator(timer)
          def get(self,request):
              #處理get請求
              return render(request,'add_publisher.html')
      

    • 加在本身寫的dispatch上(推薦)

      class Addpublisher(View):
           @method_decorator(timer)
      	def dispatch(self,request,*args,**kwargs):
              ret = super().dispatch(request,*args,**kwargs)
              return ret
      
    • 加在類上

      @method_decorator(timer,name='get')
      @method_decorator(timer,name='post')
      class Addpublisher(View):
      	def dispatch(self,request,*args,**kwargs):
              ret = super().dispatch(request,*args,**kwargs)
              return ret
          
          def get(self,request):
              pass
      
          def post(self,request,*args,**kwargs):
              pass
      

      #還能夠這樣寫
      @method_decorator(timer,name='dispatch')
          def get(self,request):
              pass
      
          def post(self,request,*args,**kwargs):
              pass
      

      不使用method_decorator也會出結果

      注:使用或者不使用method_decorator的區別

      • 不使用method_decorator

        • func :結果爲 <function AddPublisher.get at 0x000001FC8C358598>

        • args : 結果爲 (<app01.views.AddPublisher object at 0x000001FC8C432C50>, <WSGIRequest: GET '/add_publisher/'>)

      • 使用method_decorator

        • func ——》 <function method_decorator.<locals>.dec.<locals>.wrapper.<locals>.bound_func at 0x0000015185F7C0D0>

        • args ——》 (<WSGIRequest: GET '/add_publisher/'>,)

12.2.5.3request對象

  • 當一個頁面被請求時,Django就會建立一個包含本次請求原信息的HttpRequest對象。Django會將這個對象自動傳遞給響應的視圖函數,通常視圖函數約定俗成地使用 request 參數承接這個對象。官方文檔連接:https://docs.djangoproject.com/en/1.11/ref/request-response/

  • 請求經常使用值

    request.method      請求方法,請求中使用的HTTP方法的字符串表示,全大寫
    request.GET         url上攜帶的參數,包含全部HTTP  GET參數的類字典對象
    request.POST	   post請求攜帶的數據,包含全部HTTP POST參數的類字典對象
    request.path
    request.path_info   URL的路徑    不包含ip和端口  不包含參數
    request.body	    請求體  b''類型
    
  • 屬性

    django將請求報文中的請求行、頭部信息、內容主體封裝成 HttpRequest 類中的屬性。除了特殊說明的以外,其餘均爲只讀的。

    序號 屬性 結果
    1 request.scheme 表示請求方案的字符串(一般爲http或https)
    2 request.body 請求體爲 b''類型
    3 request.path 一個字符串,表示請求的路徑組件(不含域名)。
    4 request.method 一個字符串,表示請求使用的HTTP 方法。必須使用大寫
    5 request.encoding 一個字符串,表示提交的數據的編碼方式(若是爲 None 則表示使用 DEFAULT_CHARSET 的設置,默認爲 'utf-8')
    6 request.GET url上攜帶的參數,包含全部HTTP GET參數的QueryDict 對象
    7 request.POST post請求攜帶的數據,包含全部HTTP POST參數的QueryDict對象
    8 request.COOKIES 一個標準的Python 字典,包含全部的cookie。鍵和值都爲字符串
    9 request.FILES 一個相似於字典的對象,包含全部的上傳文件信息。 FILES 中的每一個鍵爲<input type="file" name="" /> 中的name,值則爲對應的數據。注意,FILES 只有在請求的方法爲POST 且提交的<form> 帶有enctype="multipart/form-data" 的狀況下才會 包含數據。不然,FILES 將爲一個空的相似於字典的對象。
    10 request.META 一個標準的Python 字典,包含全部的HTTP 首部。具體的頭部信息取決於客戶端和服務器
    11 request.user 一個 AUTH_USER_MODEL 類型的對象,表示當前登陸的用戶。(模塊)
    12 request.session 一個既可讀又可寫的相似於字典的對象,表示當前的會話。只有當Django 啓用會話的支持時纔可用。
    • request.FILES

      <form action="" method="post" enctype="multipart/form-data">
            {% csrf_token %}
          <input type="file" name="f1">
          <button>上傳</button>
      </form>
      
      def upload(request):
          if request.method == 'POST':
              #獲取文件
              print(request.FILES)  
      #<MultiValueDict: {'f1': [<InMemoryUploadedFile: S80903-190319(1).jpg (image/jpeg)>]}>
      	   f1 = request.FILES.get('f1')
              #保存文件
      	    with open(f1.name ,'wb') as f:
                  for i in f1.chunks():
                      f.write(i)
          return render(request,'upload.html')
      

  • 方法

    request.get_full_path()   URL的路徑    不包含ip和端口  包含參數
    request.get_host()      獲取主機的ip和端口
    request.is_secure()
    request.is_ajax()    判斷是都是ajax請求
    

12.2.5.4response對象

HttpResponse('字符串')          #返回字符串 content-type = 'text/html'
render(request,'模板文件名',{k1:v1})    #返回一個完整的html頁面
redirect('重定向地址')          #重定向   本質 : 響應頭Location:地址

12.2.5.5JsonResponse對象

from django.http.response import JsonResponse
import json
def json_data(request):
    data = {'name':'liming','age':18} 
    ret = HttpResponse(json.dumps(data))
    ret['Content-Type'] = application/json
    return ret
    #return HttpResponse(json.dumps(data))    #Content-Type: text/html; charset=utf-8
    return JsonResponse(data)   #Content-Type: application/json   會本身作反序列化

from django.http.response import JsonResponse
def json_data(request):
	#當被傳入數據爲列表時,要加上safe=False
    data = [1,2,3,4]
    return JsonResponse(data,safe=False)

12.2.6Django的路由系統

URL配置(URLconf)就像Django所支撐網站的目錄。它的本質是URL與要爲該URL調用的視圖函數之間的映射表。咱們就是以這種方式告訴Django,遇到哪一個URL的時候,要對應執行哪一個函數。

Django 1.11版本 URLConf官方文檔

12.2.6.1URLconf配置

  • 基本格式

    from django.conf.urls import url
    
    urlpatterns = [
         url(正則表達式, views視圖,參數,別名),
    ]
    

    參數說明

    • 正則表達式:一個正則表達式字符串

    • views視圖:一個可調用對象,一般爲一個視圖函數

    • 參數:可選的要傳遞給視圖函數的默認參數(字典形式)

    • 別名:一個可選的name參數

  • 示例

    #1.0寫法
    from django.conf.urls import url
    from . import views
    
    urlpatterns = [
        url(r'^articles/2003/$', views.special_case_2003),
        url(r'^articles/([0-9]{4})/$', views.year_archive),
        url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
        url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
    ]
    
    #2.0寫法
    from django.urls import path,re_path
    
    urlpatterns = [
        path('articles/2003/', views.special_case_2003),
        path('articles/<int:year>/', views.year_archive),
        path('articles/<int:year>/<int:month>/', views.month_archive),
        path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
    ]
    

    Django 2.0版本中的路由系統是下面的寫法官方連接:https://docs.djangoproject.com/en/1.11/topics/http/urls/

12.2.6.2正則表達式

from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/', views.blog),
    url(r'^blog/[0-9]{4}', views.blogs),
]

  • 注意事項

    • 1.urlpatterns中的元素按照書寫順序從上到下逐一匹配正則表達式,一旦匹配成功則再也不繼續

    • 2.不須要添加一個簽到的反斜槓,由於每一個URL 都有。例如,應該是^articles 而不是 ^/articles。

    • 3.每一個正則表達式前面的"r"可選但建議加上

    • 4.若要從URL中捕獲一個值,只須要在它周圍放置一對圓括號(分組匹配)

  • 補充說明

    # 是否開啓URL訪問地址後面不爲/跳轉至帶有/的路徑的配置項
    APPEND_SLASH=True
    

    若是在settings.py中設置了 APPEND_SLASH=False,此時咱們再請求 時就會提示找不到頁面。

    Django settings.py配置文件中默認沒有 APPEND_SLASH 這個參數,但 Django 默認這個參數爲 APPEND_SLASH = True。 其做用就是自動在網址結尾加'/'。

    谷歌瀏覽器清除緩存方式 : ctrl + shift + delete

12.2.6.3分組命名匹配

1.分組和命名分組

from app01 import views
urlpatterns = [
   url(r'^admin/', admin.site.urls),
   url(r'^blog/([0-9]{4})', views.blogs),  #分組,將捕獲的參數按位置傳參傳遞給視圖函數
   url(r'^blog/(?P<year>[0-9]{4})', views.blogs)  #命名組,將捕獲的參數按關鍵字傳參傳遞給視圖函數
]

項目的修改:

2.捕獲的參數永遠都是字符串

每一個在URLconf中捕獲的參數都做爲一個普通的Python字符串傳遞給視圖,不管正則表達式使用的是什麼匹配方式。例如,下面這行URLconf 中:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

傳遞到視圖函數views.year_archive() 中的year參數永遠是一個字符串類型

3.視圖函數中的指定默認值

# urls.py中
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

# views.py中,能夠爲num指定默認值
def page(request, num="1"):
    pass

在上面的例子中,兩個URL模式指向相同的view - views.page - 可是第一個模式並無從URL中捕獲任何東西。若是第一個模式匹配上了,page()函數將使用其默認參數num=「1」,若是第二個模式匹配,page()將使用正則表達式捕獲到的num值。

4.include其餘的URLconfs

urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls')),
]

app01.urls

from django.conf.urls import url
from . import views
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^blog/$', views.blog),
    url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blogs),
]

12.2.6.4傳遞額外的參數給視圖函數(瞭解)

django.conf.urls.url() 能夠接收一個可選的第三個參數,它是一個字典,表示想要傳遞給視圖函數的額外關鍵字參數。

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

在這個例子中,對於/blog/2005/請求,Django 將調用views.year_archive(request, year='2005', foo='bar')。 當傳遞額外參數的字典中的參數和URL中捕獲值的命名關鍵字參數同名時,函數調用時將使用的是字典中的參數,而不是URL中捕獲的參數。

12.2.6.5命名URL和URL反向解析

1.命名

靜態路由

from django.conf.urls import url,include
from django.contrib import admin

from . import views
urlpatterns = [
    url(r'blog',views.blog,name='blog')
]

反向解析

app01 / urls.py

from django.conf.urls import url,include
from django.contrib import admin

from . import views
urlpatterns = [
    #url(r'^admin/', admin.site.urls),
    url(r'blog',views.blog,name='blog'),
    url(r'index',views.index)
]

模板

<a href="{% url 'blog' %}">博客</a>

python文件中

from django.shortcuts import reverse
def blog(request):
    url = reverse('index')
    print(url,type(url))
    return HttpResponse('OK')

動態路由

模板中

{% url 'blogs' 2222 12 %}"   ——》  /blog/2222/12/
{% url 'blogs' year=2222 month=12 %}"   ——》  /blog/2222/12/

py文件中

from django.urls import reverse
reverse('blogs',args=('2019','06'))   ——》 /app01/blog/2019/06/ 
reverse('blogs',kwargs={'year':'2019','month':'06'})   ——》 /app01/blog/2019/06/ 

12.2.6.6命名空間模式

即便不一樣的APP使用相同的URL名稱,URL的命名空間模式也可讓你惟一反轉命名的URL。

例:project中的urls.py

from django.conf.urls import url, include
 
urlpatterns = [
    url(r'^app01/', include('app01.urls', namespace='app01')),
    url(r'^app02/', include('app02.urls', namespace='app02')),
]

app01中的urls.py

from django.conf.urls import url
from app01 import views
 
app_name = 'app01'
urlpatterns = [
    url(r'^(?P<pk>\d+)/$', views.detail, name='detail')

app02中的urls.py

from django.conf.urls import url
from app02 import views
 
app_name = 'app02'
urlpatterns = [
    url(r'^(?P<pk>\d+)/$', views.detail, name='detail')

如今,個人兩個app中 url名稱重複了,我反轉URL的時候就能夠經過命名空間的名稱獲得我當前的URL。

語法:'命名空間名稱:URL名稱'

模板中使用:

{% url 'app01:detail' pk=12 pp=99 %}

views中的函數中使用

v = reverse('app01:detail', kwargs={'pk':11})

這樣即便app中URL的命名相同,我也能夠反轉獲得正確的URL了。  

12.2.7模型(ORM)

12.2.7.1ORM概念

對象關係映射(Object Relational Mapping,簡稱ORM)模式是一種爲了解決面向對象與關係數據庫存在的互不匹配的現象的技術。簡單的說,ORM是經過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關係數據庫中,ORM在業務邏輯層和數據庫層之間充當了橋樑的做用。

ORM的優點 ORM的劣勢
ORM解決的主要問題是對象和關係的映射。它一般將一個類和一張表一一對應,類的每一個實例對應表中的一條記錄,類的每一個屬性對應表中的每一個字段。 ORM提供了對數據庫的映射,不用直接編寫SQL代碼,只需操做對象就能對數據庫操做數據。讓軟件開發人員專一於業務邏輯的處理,提升了開發效率。 ORM的缺點是會在必定程度上犧牲程序的執行效率。ORM的操做是有限的,也就是ORM定義好的操做是能夠完成的,一些複雜的查詢操做是完成不了。ORM用多了SQL語句就不會寫了,關係數據庫相關技能退化...

12.2.7.2Django中的ORM

1.經常使用字段

序號 經常使用字段 含義
1 AutoField 自增的整形字段,必填參數primary_key=True,則成爲數據庫的主鍵。無該字段時,django自動建立。一個model不能有兩個AutoField字段。
2 CharField 字符類型,必須提供max_length參數。max_length表示字符的長度。
3 IntegerField 一個整數類型。數值的範圍是 -2147483648 ~ 2147483647。
4 DateField 日期類型,日期格式爲YYYY-MM-DD,至關於Python中的datetime.date的實例。
5 DatetimeField 日期時間字段,格式爲YYYY-MM-DD HH:MM:ss[.uuuuuu],至關於Python中的datetime.datetime的實例。
6 DecimalField 10進制小數,參數:max_digits,小數總長度5位 decimal_places,小數位長度2位
7 BooleanField 布爾值類型
8 TextField 文本
  • BooleanField和DecimalField中屬性

    • auto_now = True:新增、修改數據的時候會自動保存當前的時間

    • auto_now_add = True:新增數據的時候會自動保存當前的時間

    • default 默認值

    • 三者互斥,只能取其一

  • 其它字段

    AutoField(Field)
            - int自增列,必須填入參數 primary_key=True
    
    BigAutoField(AutoField)
            - bigint自增列,必須填入參數 primary_key=True
    
            注:當model中若是沒有自增列,則自動會建立一個列名爲id的列
            from django.db import models
    
    class UserInfo(models.Model):
    	# 自動建立一個列名爲id的且爲自增的整數列
    	username = models.CharField(max_length=32)
    
    class Group(models.Model):
    	# 自定義自增列
    	nid = models.AutoField(primary_key=True)
    	name = models.CharField(max_length=32)
    
    SmallIntegerField(IntegerField):
            - 小整數 -32768 ~ 32767
    
    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正小整數 0 ~ 32767
    
    IntegerField(Field)
            - 整數列(有符號的) -2147483648 ~ 2147483647
    
    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正整數 0 ~ 2147483647
    
    BigIntegerField(IntegerField):
            - 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807
    
    NullBooleanField(Field):
            - 能夠爲空的布爾值
    
    CharField(Field)
            - 字符類型
            - 必須提供max_length參數, max_length表示字符長度
    
    
    EmailField(CharField):
            - 字符串類型,Django Admin以及ModelForm中提供驗證機制
    
    IPAddressField(Field)
            - 字符串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制
    
    GenericIPAddressField(Field)
            - 字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
            - 參數:
                protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
                unpack_ipv4, 若是指定爲True,則輸入::ffff:192.0.2.1時候,可解析爲192.0.2.1,開啓此功能,須要protocol="both"
    
    URLField(CharField)
            - 字符串類型,Django Admin以及ModelForm中提供驗證 URL
    
        SlugField(CharField)
            - 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下劃線、鏈接符(減號)
    
        CommaSeparatedIntegerField(CharField)
            - 字符串類型,格式必須爲逗號分割的數字
    
        UUIDField(Field)
            - 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證
    
        FilePathField(Field)
            - 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
            - 參數:
                    path,                      文件夾路徑
                    match=None,                正則匹配
                    recursive=False,           遞歸下面的文件夾
                    allow_files=True,          容許文件
                    allow_folders=False,       容許文件夾
    
        FileField(Field)
            - 字符串,路徑保存在數據庫,文件上傳到指定目錄
            - 參數:
                upload_to = ""      上傳文件的保存路徑
                storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
    
        ImageField(FileField)
            - 字符串,路徑保存在數據庫,文件上傳到指定目錄
            - 參數:
                upload_to = ""      上傳文件的保存路徑
                storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
                width_field=None,   上傳圖片的高度保存的數據庫字段名(字符串)
                height_field=None   上傳圖片的寬度保存的數據庫字段名(字符串)
    
        DateTimeField(DateField)
            - 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
    
        DateField(DateTimeCheckMixin, Field)
            - 日期格式      YYYY-MM-DD
    
        TimeField(DateTimeCheckMixin, Field)
            - 時間格式      HH:MM[:ss[.uuuuuu]]
    
        DurationField(Field)
            - 長整數,時間間隔,數據庫中按照bigint存儲,ORM中獲取的值爲datetime.timedelta類型
    
        FloatField(Field)
            - 浮點型
    
        BinaryField(Field)
            - 二進制類型
    

2.自定義字段

#app01/models.py
class MyCharField(models.Field):
    """
    自定義的char類型的字段類
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
 
    def db_type(self, connection):
        """
        限定生成數據庫表的字段類型爲char,長度爲max_length指定的值
        """
        return 'char(%s)' % self.max_length
#使用自定義char類型字段
class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定義的char類型的字段
    cname = MyCharField(max_length=25)

3.字段參數

1.null              數據庫中字段是否能夠爲空
2.db_index		   索引
3.blank             form表單中是否容許用戶輸入爲空
4.db_index          數據庫中字段是否能夠創建索引
5.db_column         數據庫中字段的列名
6.default           數據庫中字段的默認值
7.primary_key       數據庫中字段是否爲主鍵
8.unique            數據庫中字段是否能夠創建惟一索引
9.verbose_name      Admin中顯示的字段名稱

unique_for_date     數據庫中字段【日期】部分是否能夠創建惟一索引
unique_for_month    數據庫中字段【月】部分是否能夠創建惟一索引
unique_for_year     數據庫中字段【年】部分是否能夠創建惟一索引


editable            Admin中是否能夠編輯(不顯示)
help_text           Admin中該字段的提示信息
choices             Admin中顯示選擇框的內容,用不變更的數據放在內存中從而避免跨表操做

超級用戶的建立

from django.contrib import admin
from app01 import models

admin.site.register(models.Person)

class Person(models.Model):
    pid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32,db_column='username',verbose_name='姓名')   #會將數據庫中字段名字修改成username
    age = models.IntegerField()
    birth = models.DateTimeField(auto_now_add=True)
    phone = MyCharField(max_length=11,blank=True,null=True,verbose_name='手機號')
    gender = models.BooleanField('性別',choices=((0,'女'),(1,'男')))

4.表的參數

class Person(models.Model):
    pid = models.AutoField(primary_key=True)
    age = models.IntegerField()
    class Meta:
    	db_table = 'person'  #表名
        verbose_name = '我的信息'
        verbose_name_plural = '我的信息'
        
        # 聯合索引
	   index_together = [("name", "age"), ] # 應爲兩個存在的字段
    
    	# 聯合惟一索引
        unique_together = (("name", "age"),)   # 應爲兩個存在的字段

12.2.7.4ORM操做

官方文檔連接:https://docs.djangoproject.com/en/1.11/ref/models/querysets/

1.通常操做(必知必會13條)

#返回 Queryset
	all  filter exclude values values_list  order_by reverse  distinct()
#返回對象
	get  first  last
#返回數字
	count
#返回布爾值
	exists
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings")
    import django
    django.setup()

    from app01 import models

    #1 .all() 獲取全部的數據 ---->>QuerySet 對象列表
    ret = models.Person.objects.all()
    print(ret)   #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]>

    #2  .get(**kwargs) 獲取知足條件的一個數據  --->> 對象
        #獲取不到或多個都會報錯
    ret = models.Person.objects.get(pk=1)
    print(ret)   #1-alex

    #3  .filter(**kwargs) 獲取知足條件的全部數據 ---->>QuerySet 對象列表
        #若沒有,就返回一個空列表
    ret = models.Person.objects.filter(gender=0)
    print(ret)  #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>]>

    #4  .exclude(**kwargs) 排除不知足條件的全部數據 ---->>QuerySet 對象列表
    ret = models.Person.objects.exclude(pk=1)
    print(ret)  #<QuerySet [<Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]>

    #5  .values()               拿到對象全部的字段和字段的值---->>QuerySet [{},{}.{}] 內爲字典
       #.values('字段名稱')      拿到對象指定的字段和字段的值---->>QuerySet [{},{}.{}]
    ret = models.Person.objects.values()
    for i in ret:
        print(i)
    ret = models.Person.objects.values('pid','name')
    for i in ret:
        print(i)

    #6  .values_list()     拿到對象全部的字段和字段的值---->>QuerySet [(),().()] 內爲元組
    #   .values_list('字段名稱')     拿到對象指定的字段和字段的值---->>QuerySet [(),().()] 內爲元組
    ret = models.Person.objects.values_list()
    for i in ret:
        print(i)
    ret = models.Person.objects.values_list('pid', 'name')  #結果按()內給定位置排序
    for i in ret:
        print(i)

    #7  .order_by('字段名稱')   對查詢結果排序---->>QuerySet
    ret = models.Person.objects.all().order_by('pk')   #升序
    print(ret)    #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]>
    ret = models.Person.objects.all().order_by('-pk')  # 降序
    print(ret)     #<QuerySet [<Person: 4-li>, <Person: 3-zhang>, <Person: 2-eric>, <Person: 1-alex>]>
    ret = models.Person.objects.all().order_by('age','-pid')  # 降序
    print(ret)     #<QuerySet [<Person: 1-alex>, <Person: 3-zhang>, <Person: 2-eric>, <Person: 4-li>]>

    #8  .reverse()   反向排序(只能對已經排序的QuerySet進行排序)
    ret = models.Person.objects.all()  # 升序
    print(ret)    #<QuerySet [<Person: 1-alex>, <Person: 2-eric>, <Person: 3-zhang>, <Person: 4-li>]>
    ret = models.Person.objects.all().order_by('pk').reverse()
    print(ret)   #<QuerySet [<Person: 4-li>, <Person: 3-zhang>, <Person: 2-eric>, <Person: 1-alex>]>

    #9  .distinct()   去重(只有在PostgreSQL中支持按字段去重)徹底相同的內容才能去重
    ret = models.Person.objects.all().distinct()
    print(ret)

    #10 .count()   計數
    ret = models.Person.objects.all().count()
    print(ret)

    #11  .first()   取第一個元素,沒有就爲None
    ret = models.Person.objects.filter(pk=1).first()
    print(ret)
    ret = models.Person.objects.filter(pk=1).values().first()
    print(ret)

    #12  .last()    取最後一個元素,沒有就爲None
    ret = models.Person.objects.filter(pk=1).first()
    print(ret)

    #13  .exists()   查詢的數據是否存在
    ret = models.Person.objects.filter(pk=100).first()
    print(ret)

2.單表的雙下劃綫

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings")
import django
django.setup()

from app01 import models

ret = models.Person.objects.filter(pk=1)
print(ret)
ret = models.Person.objects.filter(pk__gt=1)  #gt  greater than  大於
print(ret)
ret = models.Person.objects.filter(pk__lt=3)  #gt  less than  小於
print(ret)
ret = models.Person.objects.filter(pk__gte=1)  #gt  greater than equal  大於等於
print(ret)
ret = models.Person.objects.filter(pk__lte=3)  #gt  less than equal 小於等於
print(ret)
ret = models.Person.objects.filter(pk__range=[2,3])    # 2<=pk<=3連續
print(ret)
ret = models.Person.objects.filter(pk__in=[1,3])    # 成員判斷,在列表裏,1和3
print(ret)
ret = models.Person.objects.filter(name__contains='a')    # 獲取name字段包含"a"的
print(ret)
ret = models.Person.objects.filter(name__contains='a')    # icontains大小寫不敏感
print(ret)
ret = models.Person.objects.filter(name__startswith='a')    # name字段以'a'開頭
print(ret)
ret = models.Person.objects.filter(name__istartswith='a')    # 大小寫不敏感
print(ret)
ret = models.Person.objects.filter(name__endswith='a')    # name字段以'a'結尾
print(ret)
ret = models.Person.objects.filter(name__iendswith='a')    # 大小寫不敏感
print(ret)
ret = models.Person.objects.filter(birth__year='2019')    #出生年份爲2019年(不支持月份和日)
print(ret)
ret = models.Person.objects.filter(birth__contaits='2019-06')    
print(ret)
ret = models.Person.objects.filter(phone__isnull=True)
print(ret)

3.外鍵操做

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings")
import django
django.setup()

from app01 import models

#基於對象的查詢
#正向
book_obj = models.Book.objects.get(title='菊花怪大戰xcq')
print(book_obj.pub)   #老男孩出版社

#反向
pub_obj = models.Publisher.objects.get(pk=1)
#print(pub_obj.book_set.all(),type(pub_obj.book_set))       #未指定related_name時,使用類名小寫_set  set理解爲集合
#print(pub_obj.books.all(),type(pub_obj.book_set))           #指定related_name='books'時

#基於字段的查詢
ret = models.Book.objects.filter(title='菊花怪大戰xcq')
print(ret)

#查詢老男孩出版社的書
ret = models.Book.objects.filter(pub__name='老男孩出版社')
print(ret)

#查詢出版'菊花怪大戰xcq'的出版社
#有related_name='books'
ret = models.Publisher.objects.filter(books__title='菊花怪大戰xcq')
print(ret)
#無related_name='books'  類名小寫
ret = models.Publisher.objects.filter(book__title='菊花怪大戰xcq')
print(ret)
#related_name='books',related_query_name='xxx'
ret = models.Publisher.objects.filter(xxx__title='菊花怪大戰xcq')
print(ret)

12.2.7.5ManyToManyField

關係管理對象

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings")
import django
django.setup()

from app01 import models

#基於對象的查詢
mjj = models.Author.objects.get(pk=1)
print(mjj.books)  #關係管理對象
print(mjj.books.all())

book_obj = models.Book.objects.filter(title='桃花俠大戰菊花怪').first()
#不指定related_name
#print(book_obj.author_set)
#print(book_obj.author_set.all())
#指定related_name
print(book_obj.authors.all())

ret = models.Author.objects.filter(books__title='桃花俠大戰菊花怪')
print(ret)
mjj = models.Author.objects.get(pk=1)
#1  .all()  所關聯的全部對象
print(mjj.books.all())

#2  .set()  設置多對多關係  [id]  [對象]
#mjj.books.set([1,2,3])
mjj.books.set(models.Book.objects.filter(pk__in=[1,2,3]))

#3  .add 添加多對對關係 [id]  [對象]
mjj.books.add(4,5)
mjj.books.add(*models.Book.objects.filter(pk__in=[4,5]))

#4  .remove 刪除多對多關係[id]  [對象]
mjj.books.remove(4,5)
mjj.books.remove(*models.Book.objects.filter(pk__in=[4,5]))

#5  .clear 清楚全部多對多關係
mjj.books.clear()

#6  .create()
obj = mjj.books.create(title='菊花怪大戰xcq',pub_id=1)
print(obj)

12.2.7.6其餘操做

1.在Python腳本中調用Django環境

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
    import django
    django.setup()

    from app01 import models

    books = models.Book.objects.all()
    print(books)

12.2.7.7聚合查詢和分組查詢

1.聚合查詢

  • aggregate()QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典,鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的 from django.db.models import Max,Min,Avg,Sum,Count

    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings")
    import django
    django.setup()
    
    from app01 import models
    from django.db.models import Max,Min,Avg,Sum,Count
    
    ret = models.Book.objects.all().aggregate(Max('price'))
    print(ret)
    ret = models.Book.objects.all().aggregate(Avg('price'))
    print(ret)
    ret = models.Book.objects.all().aggregate(Avg('price'),max=Max('price'))
    print(ret)
    ret = models.Book.objects.filter(pk__gt=2).aggregate(Avg('price'),max=Max('price'))
    print(ret)
    

2.分組查詢

咱們在這裏先複習一下SQL語句的分組:

咱們使用原生SQL語句,按照部門分組求平均工資:

select dept,avg(salary) from employee group by dept;

ORM查詢:

from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values("dept", "avg")

SQL查詢:

select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;

ORM查詢:

from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings")
import django
django.setup()

from app01 import models
from django.db.models import Max,Min,Avg,Sum,Count
#示例1:統計每一本書的做者個數
obj = models.Book.objects.annotate(Count('author')).values()  #annotate註釋
for i in obj:
    print(i)

#示例2:統計出每一個出版社買的最便宜的書的價格
#方式一:
obj = models.Publisher.objects.annotate(Min('book__price')).values()
for i in obj:
    print(i)
    
print("*"*30)
#方式二:
obj = models.Book.objects.values('pub__name').annotate(Min('price'))
for i in obj:
    print(i)

#示例3:統計不止一個做者的圖書
obj = models.Book.objects.annotate(count=Count('author')).filter(count__gt=1)
print(obj)

#示例4:根據一本圖書做者數量的多少對查詢集 QuerySet進行排序
obj = models.Book.objects.annotate(count=Count('author')).order_by('count')
print(obj)

#示例5:查詢各個做者出的書的總價格
obj = models.Author.objects.annotate(Sum('books__price')).values()
print(obj)

12.2.7.8F查詢 和Q查詢

1.F查詢

F() 的實例能夠在查詢中引用字段,來比較同一個 model 實例中兩個不一樣字段的值。

book表字段信息:

#F查詢
from django.db.models import F

#1.查詢售出大於庫存的(比較兩個字段的值)
ret = models.Book.objects.filter(sale__gt=F('kucun'))
print(ret)

#更新全部字段
obj = models.Book.objects.get(pk=1)
obj.sale=100
obj.save()

#只更新sale字段
models.Book.objects.filter(pk=1).update(sale=80)

#取某字段的值作相應操做
models.Book.objects.all().update(sale=F('sale')*2+10)

2.Q查詢

  • | 或

  • & 與

  • ~ 非

from django.db.models import Q
ret = models.Book.objects.filter(Q(pk__gt=3)|Q(pk__lt=2))
print(ret)

print('*'*30)
ret = models.Book.objects.filter(Q(Q(pk__gt=3)|Q(pk__lt=2))&Q(pub__book__kucun__gt=50))
print(ret)

12.2.7.9事務

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test61.settings")
import django
django.setup()

from app01 import models
from django.db import transaction

try:
    with transaction.atomic():   #開啓事務
    #進行一系列ORM操做
        models.Publisher.objects.create(name='xxx3')
        models.Publisher.objects.create(name='xxx4')
except Exception as e:
    print(e)

12.2.8 cookie 和 Session

https://www.cnblogs.com/maple-shaw/articles/9502602.html

12.2.8.1cookie

1.Cookie的由來

  • 你們都知道HTTP協議是無狀態的。無狀態的意思是每次請求都是獨立的,它的執行狀況和結果與前面的請求和以後的請求都無直接關係,它不會受前面的請求響應狀況直接影響,也不會直接影響後面的請求響應狀況。狀態能夠理解爲客戶端和服務器在某次會話中產生的數據,那無狀態的就覺得這些數據不會被保留。會話中產生的數據又是咱們須要保存的,也就是說要「保持狀態」。所以Cookie就是在這樣一個場景下誕生。

2.什麼叫Cookie

  • 定義:保存在瀏覽器上的一組組鍵值對

  • 特色 :

    • 1.是服務器讓瀏覽器進行設置

    • 2.瀏覽器保存在瀏覽器本地

    • 3.下次訪問時會自動攜帶

  • 能作什麼

    • 1.登陸

    • 2.保存瀏覽習慣

    • 3..簡單投票

判斷是否登陸

from django.shortcuts import render,redirect,HttpResponse

from django.views import View

class Login(View):
    def get(self,request,*args,**kwargs):
        return render(request,'login.html')

    def post(self,request,*args,**kwargs):
        username = request.POST.get('username')
        pwd = request.POST.get('pwd')
        if username == 'alex' and pwd == '123':
            url = request.GET.get('return_url')
            if url:
                ret = redirect(url)
            else:
                ret = redirect('/index/')
            #ret.set_cookie('is_login','1')
            ret.set_signed_cookie('is_login','1','aaa')  # key  value sale加鹽
            return ret
        return render(request,'login.html',{'error':'用戶名或密碼錯誤'})

    
def login_required(func):
    def inner(request,*args,**kwargs):
        #is_login = request.COOKIES.get('is_login')
        is_login = request.get_signed_cookie('is_login','aaa',default='')
        url = request.path_info
        if is_login != '1':
            return redirect('/login/?return_url={}'.format(url))
        #已登陸
        ret = func(request,*args,**kwargs)
        return ret
    return inner

@login_required
def index(request):
    #獲取到cookie
    is_login = request.COOKIES.get('is_login')
    if is_login != '1':
        return redirect('/login/')
    return HttpResponse('首頁')

@login_required
def home(request):
    return HttpResponse('home')
  • 1.獲取Cookie

    request.COOKIES['key']
    request.get_signed_cookie('key', default=RAISE_ERROR, salt='', max_age=None)
    

    get_signed_cookie方法的參數:

    • default: 默認值

    • salt: 加密鹽

    • max_age: 後臺控制過時時間

  • 2.設置Cookie

    rep = HttpResponse(...)
    rep = render(request, ...)
    
    rep.set_cookie(key,value,...)
    rep.set_signed_cookie(key,value,salt='加密鹽',...)
    

    參數:

    • key, 鍵

    • value='', 值

    • max_age=None, 超時時間

    • expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)

    • path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie能夠被任何url的頁面訪問

    • domain=None, Cookie生效的域名

    • secure=False, https傳輸

    • httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)

  • 3.刪除Cookie

    def logout(request):
        ret = redirect("/login/")
        ret.delete_cookie("is_login")  # 刪除用戶瀏覽器上以前設置的user的cookie值
        return ret
    

12.2.8.2Session

  • 定義:保存在服務器上的一組組鍵值對,必須依賴與cookie使用

1.爲何要有Session

  • 1.cookie保存在瀏覽器本地

  • 2.大小個數受到限制(4096)

2.操做session

  • 設置

    • request.session [key] = value

  • 獲取

    • request.session [key]

    • request.session.get(key)

3.Session流程解析

4.Django中Session相關方法

# 獲取、設置、刪除Session中數據
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在則不設置
del request.session['k1']


# 全部 鍵、值、鍵值對
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

# 會話session的key
request.session.session_key

# 將全部Session失效日期小於當前日期的數據刪除(expired:過時)
request.session.clear_expired()

# 檢查會話session的key在數據庫中是否存在
request.session.exists("session_key")   

# 刪除當前會話的全部Session數據,不刪除會話的Cookie
request.session.delete()
  
# 刪除當前的會話數據,並刪除會話的Cookie。
request.session.flush() 
    這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問
    例如,django.contrib.auth.logout() 函數中就會調用它。

# 設置會話Session和Cookie的超時時間
request.session.set_expiry(value)
    * 若是value是個整數,session會在些秒數後失效。
    * 若是value是個datatime或timedelta,session就會在這個時間後失效。
    * 若是value是0,用戶關閉瀏覽器session就會失效。
    * 若是value是None,session會依賴全局session失效策略。

5.Django中的Session配置

1. 數據庫Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默認)

2. 緩存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir() 

4. 緩存+數據庫
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其餘公用設置項:
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路徑(默認)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默認)
SESSION_COOKIE_SECURE = False                            # 是否Https傳輸cookie(默認)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http傳輸(默認)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默認)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否關閉瀏覽器使得Session過時(默認)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次請求都保存Session,默認修改以後才保存(默認)
from django.conf import global_settings

12.2.9CSRF

  • CSRF(Cross-site request forgery)跨站請求僞造,也被稱爲「One Click Attack」或者Session Riding,一般縮寫爲CSRF或者XSRF,是一種對網站的惡意利用

    隱藏:

12.2.9.1CSRF裝飾器

from django.views.decorators.csrf import csrf_exempt,csrf_protect,ensure_csrf_cookie
from django.utils.decorators import method_decorator
csrf_exempt   		#某個視圖不須要進行csrf校驗
csrf_protect		#某個視圖須要進行csrf校驗
ensure_csrf_cookie   #確保生成csrf的cookie

12.2.9.2CSRF功能

  • 1.crsf中間件中執行process_request方法

    • 1.從cookie中獲取到csrftoken

    • 2.csrftoken中的值放到request.META中

  • 2.執行process_view方法

    • 1.查詢視圖函數是否使用csrf_exempt裝飾器,使用了就不進行csrf校驗

    • 2.判斷請求方式

      • 若是是 'GET', 'HEAD', 'OPTIONS', 'TRACE' ,不進行csrf校驗

      • 其餘請求方式進行crsf校驗 :

        • 獲取cookie中的csrftoken中的值

        • 獲取csrfmiddlewaretoken的值

          • 能獲取到----->>request_csrf_token

          • 獲取不到----->>獲取請求頭中的 X-csrftoken的值----->>request_csrf_token

      • 比較request_csrf_toke和cookief中的csrftoken,比較成功接受請求,比較不成功,拒絕請求

12.2.10ajax

12.2.10.1.AJAX準備知識:JSON

  • 什麼是JSON

    1. JSON 指的是 JavaScript 對象表示法(JavaScript Object Notation)
    2. JSON 是輕量級的文本數據交換格式
    3. JSON 獨立於語言 
    4. JSON 具備自我描述性,更易理解
    

    不合格的json對象:

    { name: "張三", 'age': 32 }         // 屬性名必須使用雙引號
    [32, 64, 128, 0xFFF]             // 不能使用十六進制值
    { "name": "張三", "age": undefined }    // 不能使用undefined
    { "name": "張三",
      "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
      "getName":  function() {return this.name;}  // 不能使用函數和日期對象
    }
    
  • 和XML的比較

    • JSON 格式有兩個顯著的優勢:書寫簡單,一目瞭然;符合 JavaScript 原生語法,能夠由解釋引擎直接處理,不用另外添加解析代碼。因此,JSON迅速被接受,已經成爲各大網站交換數據的標準格式,並被寫入ECMAScript 5,成爲標準的一部分。XML和JSON都使用結構化方法來標記數據

    • JSON 簡單的語法格式和清晰的層次結構明顯要比 XML 容易閱讀,而且在數據交換方面,因爲 JSON 所使用的字符要比 XML 少得多,能夠大大得節約傳輸數據所佔用得帶寬。

12.2.10.2AJAX簡介

AJAX(Asynchronous Javascript And XML)翻譯成中文就是「異步的Javascript和XML」。即便用Javascript語言與服務器進行異步交互,傳輸的數據爲XML(固然,傳輸的數據不僅是XML)。

AJAX 不是新的編程語言,而是一種使用現有標準的新方法。

AJAX 最大的優勢是在不從新加載整個頁面的狀況下,能夠與服務器交換數據並更新部分網頁內容。(這一特色給用戶的感覺是在不知不覺中完成請求和響應過程)

AJAX 不須要任何瀏覽器插件,但須要用戶容許JavaScript在瀏覽器上執行。

  • 同步交互:客戶端發出一個請求後,須要等待服務器響應結束後,才能發出第二個請求;

  • 異步交互:客戶端發出一個請求後,無需等待服務器響應結束,就能夠發出第二個請求。

總結 : ajax就是使用js的技術發請求和接收響應的

特色:1.異步,2.局部刷新,3.傳輸數據量少

  • 發請求的方式

    • 地址欄輸入地址 GET

    • form請求 GET/POST

      • 1.method 請求方式 action 地址

      • 2.標籤要有name屬性,有的要有value

      • 3.要有一個input type='submit' 或者button

    • a標籤 GET

    • ajax

使用 : jq發ajax

例一:頁面輸入兩個整數,經過AJAX傳輸到後端計算出結果並返回

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>

<input type="text" name="i1" >+
<input type="text" name="i2" >=
<input type="text" name="i3" >
<button id="b1" >計算</button>


<script src="/static/jquery.js"></script>>
<script>
   $("#b1").click(function(){
       //發ajax請求
       $.ajax({
           url:'/calc/',
           type:'post',
           data:{
               a:$("[name='i1']").val(),
               b:$("[name='i2']").val()
          },
           success:function(res){
               $("[name='i3']").val(res)
          },
            error:function(error){
               console.log(error)
          }
      })
  })
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse

def calc(request):
   a = request.POST.get('a')
   b = request.POST.get('b')
   c = int(a) + int(b)
   return HttpResponse(c)

例二:異步的實現

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>

<input type="text" name="i1" >+
<input type="text" name="i2" >=
<input type="text" name="i3" >
<button id="b1" >計算</button>
<br>
<input type="text" name="ii1" >+
<input type="text" name="ii2" >=
<input type="text" name="ii3" >
<button id="b2">計算</button>

<script src="/static/jquery.js"></script>>
<script>
   $("#b1").click(function(){
       //發ajax請求
       $.ajax({
           url:'/calc/',
           type:'post',
           data:{
               a:$("[name='i1']").val(),
               b:$("[name='i2']").val()
          },
           success:function(res){
               $("[name='i3']").val(res)
          },
           error:function(error){
               console.log(error)
          }
      })
  });

   $("#b2").click(function(){
       //發ajax請求
       $.ajax({
           url:'/calc2/',
           type:'post',
           data:{
               a:$("[name='ii1']").val(),
               b:$("[name='ii2']").val()
          },
           success:function(res){
               $("[name='ii3']").val(res)
          },
           error:function(error){
               console.log(error)
          }
      })
  })
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse

import time
def calc(request):
    a = request.POST.get('a')
    b = request.POST.get('b')
    c = int(a) + int(b)
    time.sleep(2)
    return HttpResponse(c)

def calc2(request):
    a = request.POST.get('a')
    b = request.POST.get('b')
    c = int(a) + int(b)
    return HttpResponse(c)

例三:列表元素的獲取(法一)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button id="b3">參數</button>

<script src="/static/jquery.js"></script>>
<script>
     $("#b3").click(function(){
        //發ajax請求
        $.ajax({
            url:'/test/',
            type:'post',
            data:{
                name: 'alex',
                age:30,
                hobby:['唱','跳','rap'],
            },
            success:function(res){
                console.log(res)
            },
            error:function(error){
                console.log(error)
            }
        })
    })
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse

def test(request):
    print(request.POST)
    ret = request.POST.get('hobby')
    print(ret)
    ret = request.POST.get('hobby[]')
    print(ret)
    ret = request.POST.getlist('hobby[]')
    print(ret)
    return HttpResponse('OK')

例四:列表元素的獲取(法二)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<button id="b3">參數</button>

<script src="/static/jquery.js"></script>>
<script>

     $("#b3").click(function(){
        //發ajax請求
        $.ajax({
            url:'/test/',
            type:'post',
            data:{
                name: 'alex',
                age:30,
                hobby:JSON.stringify(['唱','跳','rap']),
            },
            success:function(res){
                console.log(res)
            },
            error:function(error){
                console.log(error)
            }
        })
    })
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse

import json
def test(request):
    print(request.POST)

    ret = request.POST.get('hobby')
    print(ret)
    obj = json.loads(ret)
    print(obj,type(obj))

    return HttpResponse('OK')

例五:上傳文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<input type="file" id="f1">
<button id="b1">上傳</button>

<script src="/static/jquery.js"></script>>
<script>
    $("#b1").click(function(){
        var formobj = new FormData();
        formobj.append('file',document.getElementById('f1').files[0]);
        formobj.append('name','alex');
        //發ajax請求
        $.ajax({
            url:'/upload/',
            type:'post',
            data:formobj,
            processData:false,    #不處理編碼方式
            contentType:false,    #
            success:function(res){
                $("[name='i3']").val(res)
            },
            error:function(error){
                console.log(error)
            }
        })
    })
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse

def upload(request):
    print(request.POST)
    print(request.FILES)
    return render(request,'upload.html')

例六:ajax經過csrf校驗

  • 1.確保有csrftoken的cookie

    在頁面中使用{% csrf_token%}

  • 2.加裝飾器ensure_csrf_cookie

    from django.views.decorators.csrf import csrf_exempt,csrf_protect,ensure_csrf_cookie

    • 1.給data中添加crsfmiddlewaretoken鍵值對

      data:{
          'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val(),
          a:$("[name='i1']").val(),
          b:$("[name='i2']").val()
      },
      

      2.加請求頭

      header:{
                      'x-csrftoken':$('[name="csrfmiddlewaretoken"]').val(),
                  },
      

      3.使用文件(static/js)

      function getCookie(name) {
          var cookieValue = null;
          if (document.cookie && document.cookie !== '') {
              var cookies = document.cookie.split(';');
              for (var i = 0; i < cookies.length; i++) {
                  var cookie = jQuery.trim(cookies[i]);
                  // Does this cookie string begin with the name we want?
                  if (cookie.substring(0, name.length + 1) === (name + '=')) {
                      cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                      break;
                  }
              }
          }
          return cookieValue;
      }
      var csrftoken = getCookie('csrftoken');
      
      function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
      }
      
      $.ajaxSetup({
        beforeSend: function (xhr, settings) {
          if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
          }
        }
      });
      

12.2.11簡單的登陸的實例

<body>

<div class="container">

    <form class="form-signin" action="" method="post">
        <h2 class="form-signin-heading">Please sign in</h2>
        <label for="inputEmail" class="sr-only">Email address</label>
        <input type="text" id="inputEmail" class="form-control" placeholder="Email address" required="" autofocus="" name="username">
        <label for="inputPassword" class="sr-only">Password</label>
        <input type="password" id="inputPassword" class="form-control" placeholder="Password" name="passward">
        <div class="checkbox">
            <label>
                <input type="checkbox" value="remember-me"> Remember me
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    </form>

</div> <!-- /container -->

</body>

form表單提交數據注意的問題:

  • 1.提交的地址action="",請求的方式 method="post"

  • 2.全部input框都有name屬性

  • 3.有一個input框的type="submit" 或者 有一個button

運行程序會出現如下狀況

提交POST請求,把settings中MIDDLEWARE的'django.middleware.csrf.CsrfViewMiddleware'註釋掉

from django.conf.urls import url
from django.contrib import admin

from django.shortcuts import render

def index(request):
    return render(request,"index.html")

def login(request):
    print(request.method,type(request.method))
    
    return render(request,"login.html")


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),
    url(r'^login/', login),
]

點擊路徑回車,會獲得

輸入帳號密碼提交,會獲得

from django.conf.urls import url
from django.contrib import admin

from django.shortcuts import render

def index(request):
    return render(request,"index.html")

def login(request):
    #print(request.method,type(request.method))
    if request.method == 'GET':
        return render(request,"login.html")
    elif request.method == 'POST':
        #獲取form表單提交的數據
        print(request.POST)
        #而後驗證用戶名和密碼
        #驗證成功,跳轉到index頁面
        #驗證不成功,從新登錄
        
        
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),
    url(r'^login/', login),
]

from django.conf.urls import url
from django.contrib import admin

from django.shortcuts import render

def index(request):
    return render(request,"index.html")

def login(request):
    #print(request.method,type(request.method))
    if request.method == 'POST':
        #獲取form表單提交的數據
        print(request.POST)
        username = request.POST['username']
        
        print(username,type(username))
        #而後驗證用戶名和密碼
        #驗證成功,跳轉到index頁面
        #驗證不成功,從新登錄
        return render(request,"login.html")

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),
    url(r'^login/', login),
]

from django.conf.urls import url
from django.contrib import admin

from django.shortcuts import render,redirect

def index(request):
    return render(request,"index.html")

def login(request):
    #print(request.method,type(request.method))
    if request.method == 'POST':
        #獲取form表單提交的數據
        print(request.POST)
        username = request.POST['username']
        password = request.POST['password']
        print(username,type(username))
        #而後驗證用戶名和密碼
        if username == 'alex' and password == 'sb':
        #驗證成功,跳轉到index頁面
            return redirect('https://www.baidu.com/')
        #驗證不成功,從新登錄
    	return render(request,"login.html")

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),
    url(r'^login/', login),
]

# request.method     #請求方式 POST  GET
# request.POST       #form表單提交的數據,去數據方式 []  或者  .get
.get好處,有默認值
from django.shortcuts import redirect
return redirect('https://www.baidu.com/')

12.2.12圖書管理系統

12.2.12.1出版社管理

  • 1.建立項目

  • 2.建立mysql數據庫

  • 3.更改配置

    在init中添加代碼

  • 4.建表

    class publisher(models.Model):
        #默認生成id
        #設置主鍵
        pid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        addr = models.CharField(max_length=32)
    

    在終端裏作數據庫遷移命令

     

     

  • 1.展現

    設計url

    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        
        #添加出版社表信息路徑
        url(r'^publisher_list/', views.publisher_list),
    ]
    

     

    寫函數

    from django.shortcuts import render
    from app01 import models
    #展現出版社
    def publisher_list(request):
        #從數據庫中查詢出版社信息
        all_publisher = models.publisher.objects.all()
        #返回一個包含出版社信息的頁面
        return render(request,'publisher_list.html',{'all_publisher':all_publisher})
    

     

    寫模板

    <body>
        <h1>出版社列表</h1>
    
        {% comment %}{{all_publisher}}{% endcomment %}
    
        <table border="1">
            <thead>
                <tr>
                    <th>序號</th>
                    <th>id</th>
                    <th>名稱</th>
                </tr>
            </thead>
            <tbody>
            {% for publisher in all_publisher %}
                <tr>
                    <td>{{forloop.counter}}</td>
                    <td>{{publisher.pk}}</td>
                    <td>{{publisher.name}}</td>
                </tr>
            {% endfor%}
            </tbody>
        </table>
    </body>
    </html>
    
  • 2.新增

    #基礎
    
    from django.shortcuts import render,redirect
    from app01 import models
    #展現出版社
    def publisher_list(request):
        #從數據庫中查詢出版社信息
        all_publisher = models.publisher.objects.all()
        #返回一個包含出版社信息的頁面
        return render(request,'publisher_list.html',{'all_publisher':all_publisher})
    
    #新增出版社
    def add_publisher(request):
        #對請求方式進行判斷
        if request.method == 'POST':
            #處理POST請求
            #獲取出版社名稱
            publisher_name = request.POST.get('publisher_name')
            #判斷出版社名稱是否重複
            if models.publisher.objects.filter(name=publisher_name):
                return render(request, 'add_publisher.html',{'error':"出版社名稱已存在"})
            #判斷輸入值是否爲空
            if not publisher_name:
                return render(request, 'add_publisher.html', {'error': "輸入不能爲空"})
            #使用orm將數據插入到數據庫中
            obj = models.publisher.objects.create(name=publisher_name)
            print(obj.pk,obj.name)
            #跳轉到展現出版社界面
            return redirect('/publisher_list/')
        #返回一個包含form表單的頁面
        return render(request,'add_publisher.html')
    
    #優化版
    
    from django.shortcuts import render,redirect
    from app01 import models
    #展現出版社
    def publisher_list(request):
        #從數據庫中查詢出版社信息
        all_publisher = models.publisher.objects.all().order_by("pk")
        #返回一個包含出版社信息的頁面
        return render(request,'publisher_list.html',{'all_publisher':all_publisher})
    
    #新增出版社
    def add_publisher(request):
        #對請求方式進行判斷
        error = ''
        if request.method == 'POST':
            #處理POST請求
            #獲取出版社名稱
            publisher_name = request.POST.get('publisher_name')
            #判斷出版社名稱是否重複
            if models.publisher.objects.filter(name=publisher_name):
                error = "出版社名稱已存在"
            #判斷輸入值是否爲空
            if not publisher_name:
                error = "輸入不能爲空"
            if not error:
                #使用orm將數據插入到數據庫中
                obj = models.publisher.objects.create(name=publisher_name)
                #print(obj.pk,obj.name)
                #跳轉到展現出版社界面
                return redirect('/publisher_list/')
        #返回一個包含form表單的頁面
        return render(request,'add_publisher.html',{'error':error})
    

  • 3.刪除

    obj_list = models.Publisher.objects.filter(pk=pk)
    obj_list.delete()
    
    obj = models.Publisher.objects.get(pk=pk)
    obj.delete()
    
    #刪除出版社
    def del_publisher(request):
        #獲取要刪除的去數據
        pk = request.GET.get('id')
        obj_list = models.publisher.objects.filter(pk=pk)
        if not obj_list:
            #沒有要刪除的數據(直接輸入)
            return HttpResponse('要刪除的數據不存在')
        #刪除該數據
        #obj.delete()
        obj_list.delete()
        #跳轉到展現頁面
        return redirect('/publisher_list/')
    

  • 4.編輯

    #編輯出版社
    def edit_publisher(request):
        error = ''
        #查找要編輯的頁面
        pk = request.GET.get('id')
        obj_list = models.publisher.objects.filter(pk=pk)
        if not obj_list:
            return HttpResponse('要編輯的數據不存在')
        obj = obj_list[0]
        if request.method == 'POST':
            #處理POST請求
            #獲取新提交的出版社名稱
            publisher_name = request.POST.get('publisher_name')
            if models.publisher.objects.filter(name=publisher_name):
                #新修改的名稱已存在
                error = '新修改的名稱已存在'
            if obj.name == publisher_name:
                error = '名稱未修改'
            if not publisher_name:
                error = '提交名稱不能爲空'
            if not error:
                #修改數據
                obj.name = publisher_name
                #保存數據到數據庫
                obj.save()
                #跳轉到出版社展現頁面
                return redirect('/publisher_list/')
    
        #返回一個包含原始數據的頁面
        return render(request,'edit_publisher.html',{'obj':obj,'error':error})
    

12.2.12.2書籍管理

  • 1.結構

    class Book(models.Model):
        name = models.CharField(max_length=32,unique=True)
        pub_id = models.ForeignKey('publishe',on_delete=models.CASCADE) #models.CASCADE 級聯刪除      
    #on_delete參數
    #1.11版本以前不用填寫.默認是on_delete=models.CASCADE),但在2.0版本以後必填
    #on_delete=models.SET(value)設置成某個值    SET_DEFAULT 設置成默認值   SET_NULL
    

    執行數據庫遷移命令

  • #展現書籍
    def book_list(request):
        #獲取全部書籍對象
        books = models.Book.objects.all()
       
        for i in books:
            i,type(i)
           	i.pk, type(i.pk)
            i.name, type(i.name)
            i.pub, type(i.pub)   #所關聯對象
            i.pub.pk			#所關聯對象id
            i.pub_id			#所關聯對象id(推薦使用)
    
    #新增書籍
    def add_book(request):
       if request.method == 'POST':
           #獲取提交數據
           new_name = request.POST.get('new_name')
           pub_id = request.POST.get('pub')
           #插入到數據庫中
           models.Book.objects.create(name=new_name,pub=models.publisher.objects.get(pk=pub_id))
           #跳轉到展現頁面
           return redirect('/book_list/')
       #全部的出版社信息
       publisher = models.publisher.objects.all()
       return render(request,'add_book.html',{'publisher':publisher})
    #編輯書籍
    def edit_book(request):
       #查詢要編輯書籍的對象
       pk=request.GET.get('pk')
       edit_obj=models.Book.objects.get(pk=pk)

       if request.method == 'POST':
           #獲取提交數據
           new_name=request.POST.get('new_name')
           pub_id = request.POST.get('pub')
           #編輯數據
           edit_obj.name=new_name
           edit_obj.pub_id = pub_id   #id
           #edit_obj.pub= models.publisher.objects.get(pub=pub_id) 對象
           #保存到數據庫
           edit_obj.save()
           #重定向到展現界面
           return redirect('/book_list/')
       #查詢全部出版社
       publisher = models.publisher.objects.all()
       return render(request,'edit_book.html',{'edit_obj':edit_obj,'publisher':publisher})

12.2.12.3做者表

  • 建表

    class Author(models.Model):
        name = models.CharField(max_length=32)
        books = models.ManyToManyField('Book')
    

  • 展現做者

    設計url

    url(r'^author_list/', views.author_list),
    

    寫函數

    #展現做者
    def author_list(request):
        #查詢全部做者
        all_author = models.Author.objects.all()
        for author in all_author:
            print(author)
            print(author.pk)
            print(author.name,type(author.name))
            print(author.books,type(author.books))  #關係管理對象
            print(author.books.all(), type(author.books.all()))
            print("*"*20)
        return render(request,'author_list.html',{'all_author':all_author})
    

    寫模板

    <body>
        <h1>做者列表</h1>
    <table border="1">
        <thead>
            <tr>
                <th>序號</th>
                <th>做者id</th>
                <th>做者姓名</th>
                <th>表明做</th>
            </tr>
        </thead>
        <tbody>
            {% for author in all_author %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td>{{ author.pk }}</td>
                    <td>{{ author.name }}</td>
                    <td>
                        {% for book in  author.books.all %}
                             {% if forloop.last %}   
                                <<{{ book.name }}>>
                              {% else %}
                                 <<{{ book.name }}>>,
                             {% endif %}
    
                        {% endfor %}
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
    </body>
    
  • 增長

    class Author(models.Model):
        name = models.CharField(max_length=32)
        books = models.ManyToManyField('Book')
    

    寫函數

    #增長做者
    def add_author(request):
        if request.method == 'POST':
            #獲取post請求提交數據
            author_name = request.POST.get('author_name')
            books = request.POST.get('books')
            """
            print(request.POST)
            print(author_name)
            print(books)
            """
            #存到數據庫
            author_obj = models.Author.objects.create(name=author_name )  #只插入book表中內容
            author_obj.books.set(books)   #設置做者和書籍多對對關係
            #跳轉到展現界面
            return redirect('/author_list/')
        #查詢全部書籍
        all_books = models.Book.objects.all()
        return render(request,'add_author.html',{"all_books":all_books})
    

    寫模板

    <body>
    <form action="" method="POST">
        <p>做者姓名:<input type="text" name="author_name"></p>
        <p>做品:
            <select name="books" id="" multiple>
                <option value=""></option>
                {% for book in all_books %}
                    <option value="{{ book.pk }}">{{ book.name }}</option>
                {% endfor %}
            </select>
        </p>
        <button>提交</button>
    </form>
    </body>
    
  • 編輯

    class Author(models.Model):
        name = models.CharField(max_length=32)
        books = models.ManyToManyField('Book')
    

    寫對象

    #編輯做者
    def edit_author(request):
        #查詢要編輯做者對象
        pk = request.GET.get('pk')
        author_obj = models.Author.objects.get(pk=pk)
        if request.method == 'POST':
            #獲取提交的數據
            name = request.POST.get('author_name')
            books = request.POST.getlist('books')
            #修改對象的數據
            author_obj.name = name
            author_obj.save()
            #多對多的關係
            author_obj.books.set(books)  #每次從新設置
            #重定向
            return redirect('/author_list/')
        #查詢全部書籍
        all_book = models.Book.objects.all()
        return render(request,'edit_author.html',{'author_obj':author_obj,'all_book':all_book})
    
    <body>
    
    <form action="" method="post">
        <p>做者姓名:<input type="text" name="author_name" value="{{ author_obj.name }}"></p>
        <p>做品:
            <select name="books" id="" multiple>
                {% for book in all_book %}
    
                    {% if book in author_obj.books.all %}
                        <option selected value="{{ book.pk }}">{{ book.name }}</option>
                    {% else %}
                        <option value="{{ book.pk }}">{{ book.name }}</option>
                    {% endif %}
    
                {% endfor %}
            </select>
        </p>
        <button>提交</button>
    </form>
    </body>
    

12.2.12.4django設置多對多關係三種方法

  • 1.django幫咱們生成第三張表

    class Author(models.Model):
        name = models.CharField(max_length=32)
        books = models.ManyToManyField('Book')  # 不在Author表中生產字段,生產第三張表
    
  • 2.本身建立第三張表

    class AuthorBook(models.Model):
        author = models.ForeignKey(Author, on_delete=models.CASCADE)
        book = models.ForeignKey(Book, on_delete=models.CASCADE)
        
        #可任意添加字段
        date = models.DateField()
    
  • 3.自建的表和 ManyToManyField 聯合使用

    class Author(models.Model):
        name = models.CharField(max_length=32)
        books = models.ManyToManyField('Book',through='AuthorBook',through_fields=['author','book'])  # 不在Author表中生產字段,生產第三張表
    
    
    class AuthorBook(models.Model):
        author = models.ForeignKey(Author,related_name='a', on_delete=models.CASCADE)
        book = models.ForeignKey(Book,related_name='b', on_delete=models.CASCADE)
        
        tuijian = models.ForeignKey(Author, on_delete=models.CASCADE)
        date = models.DateField()
    

12.2.12.5

 

12.2.12.5升級版

def delete(request,table,pk):
    print(table,pk)
    #查找對應對象
    table_class = getattr(models,table.capitalize())
    table_class.objects.filter(pk=pk).delete()   #刪除
    #重定向到對應展現界面
    return redirect(reverse(table))

12.2.13 form組件

https://www.cnblogs.com/maple-shaw/articles/9537309.html

12.2.12.1 form組件的功能

# 1.生成input標籤
#2.對提供的數據進行校驗
#3.提供錯誤提示
  • 1.定義form組件

    from django import forms
    
    class Regform(forms.Form):
        username = forms.CharField(label='用戶名')    #label 改成中文
        pwd = forms.CharField(label='密碼',widget=forms.PasswordInput)  #widget 密文
    
  • 2.使用

    視圖裏

    def reg2(request):
        form_obj = Regform()
        if request.method == 'POST':
            form_obj = Regform(data=request.POST)
            if form_obj.is_valid():   #對數據進行校驗
                #插入數據
                return HttpResponse('註冊成功')
        return render(request,'reg2.html',{'form_obj':form_obj})
    

    模板裏

    <form action="" method="post" novalidate> {# novalidate:不在前端進行校驗 #}
        {% csrf_token %}
        {{ form_obj.as_p }}     # #生成一個個p標籤(包含input  label)
        {{ form_obj.errors }}
        <button>註冊</button>
    </form>
    

    {{ form_obj.as_p }}     # 生成一個個p標籤(包含input  label)
    {{ form_obj.errors }}   #生成全部錯誤
    {{ form_obj.username }}  #一個字段所對應的input框
    {{ form_obj.username.label }}   #該字段的中文提示
    {{ form_obj.username.id_for_label }}   #該字段input的id
    {{ form_obj.username.errors }}    #該字段的全部錯誤
    {{ form_obj.username.errors.0 }}	#該字段的第一個錯誤
    

12.2.12.2經常使用字段與插件

  • 1.initial

    初始值,input框裏面的初始值

    class LoginForm(forms.Form):
        username = forms.CharField(
            min_length=8,
            label="用戶名",
            initial="張三"  # 設置默認值
        )
        pwd = forms.CharField(min_length=6, label="密碼")
    
  • 2.error_messages

    錯誤提示

    class LoginForm(forms.Form):
        username = forms.CharField(
            min_length=8,
            label="用戶名",
            initial="張三",
            required=True,
            error_messages={
                "required": "不能爲空",
                "invalid": "格式錯誤",
                "min_length": "用戶名最短8位"
            }
        )
        pwd = forms.CharField(min_length=6, label="密碼")
    
  • 3.password

    密文顯示

    class LoginForm(forms.Form):
        pwd = forms.CharField(label='密碼',widget=forms.PasswordInput)  #widget 密文
    
  • 4. radioSelect

    class LoginForm(forms.Form):
        gender = forms.fields.ChoiceField(
            choices=((1, "男"), (2, "女"), (3, "保密")),
            label="性別",
            initial=3,
            widget=forms.RadioSelect
        )
    
  • 5.單選Select

    class LoginForm(forms.Form):
        ...
        hobby = forms.fields.ChoiceField(
            choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ),
            label="愛好",
            initial=3,
            widget=forms.widgets.Select()
        )
    
  • 6.多選Select

    lass LoginForm(forms.Form):
        ...
        hobby = forms.fields.MultipleChoiceField(
            choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ),
            label="愛好",
            initial=[1, 3],
            widget=forms.MultipleChoiceField(
            choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ),)
        )
    
  • 7.單選checkbox

    class LoginForm(forms.Form):
        ...
        keep = forms.fields.ChoiceField(
            label="是否記住密碼",
            initial="checked",
            widget=forms.widgets.CheckboxInput()
        )
    
  • 8.多選checkbox

    class LoginForm(forms.Form):
        ...
        hobby = forms.fields.MultipleChoiceField(
            choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),
            label="愛好",
            initial=[1, 3],
            widget=forms.widgets.CheckboxSelectMultiple()
        )
    

12.2.12.3Django Form全部內置字段

Field
   required=True,               是否容許爲空
   widget=None,                 HTML插件
   label=None,                  用於生成Label標籤或顯示內容
   initial=None,                初始值
   help_text='',                幫助信息(在標籤旁邊顯示)
   error_messages=None,         錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'}
   validators=[],               自定義驗證規則
   localize=False,              是否支持本地化
   disabled=False,              是否能夠編輯
   label_suffix=None            Label內容後綴


CharField(Field)
   max_length=None,             最大長度
   min_length=None,             最小長度
   strip=True                   是否移除用戶輸入空白

IntegerField(Field)
   max_value=None,              最大值
   min_value=None,              最小值

FloatField(IntegerField)
  ...

DecimalField(IntegerField)
   max_value=None,              最大值
   min_value=None,              最小值
   max_digits=None,             總長度
   decimal_places=None,         小數位長度

BaseTemporalField(Field)
   input_formats=None          時間格式化  

DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12

DurationField(Field)            時間間隔:%d %H:%M:%S.%f
  ...

RegexField(CharField)
   regex,                      自定製正則表達式
   max_length=None,            最大長度
   min_length=None,            最小長度
   error_message=None,         忽略,錯誤信息使用 error_messages={'invalid': '...'}

EmailField(CharField)      
  ...

FileField(Field)
   allow_empty_file=False     是否容許空文件

ImageField(FileField)      
  ...
   注:須要PIL模塊,pip3 install Pillow
   以上兩個字典使用時,須要注意兩點:
       - form表單中 enctype="multipart/form-data"
       - view函數中 obj = MyForm(request.POST, request.FILES)

URLField(Field)
  ...


BooleanField(Field)  
  ...

NullBooleanField(BooleanField)
  ...

ChoiceField(Field)
  ...
   choices=(),                選項,如:choices = ((0,'上海'),(1,'北京'),)
   required=True,             是否必填
   widget=None,               插件,默認select插件
   label=None,                Label內容
   initial=None,              初始值
   help_text='',              幫助提示


ModelChoiceField(ChoiceField)
  ...                        django.forms.models.ModelChoiceField
   queryset,                  # 查詢數據庫中的數據
   empty_label="---------",   # 默認空顯示內容
   to_field_name=None,        # HTML中value的值對應的字段
   limit_choices_to=None      # ModelForm中對queryset二次篩選
   
ModelMultipleChoiceField(ModelChoiceField)
  ...                        django.forms.models.ModelMultipleChoiceField


   
TypedChoiceField(ChoiceField)
   coerce = lambda val: val   對選中的值進行一次轉換
   empty_value= ''            空值的默認值

MultipleChoiceField(ChoiceField)
  ...

TypedMultipleChoiceField(MultipleChoiceField)
   coerce = lambda val: val   對選中的每個值進行一次轉換
   empty_value= ''            空值的默認值

ComboField(Field)
   fields=()                  使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式
                              fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])

MultiValueField(Field)
   PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用

SplitDateTimeField(MultiValueField)
   input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
   input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']

FilePathField(ChoiceField)     文件選項,目錄下文件顯示在頁面中
   path,                      文件夾路徑
   match=None,                正則匹配
   recursive=False,           遞歸下面的文件夾
   allow_files=True,          容許文件
   allow_folders=False,       容許文件夾
   required=True,
   widget=None,
   label=None,
   initial=None,
   help_text=''

GenericIPAddressField
   protocol='both',           both,ipv4,ipv6支持的IP格式
   unpack_ipv4=False          解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1PS:protocol必須爲both才能啓用

SlugField(CharField)           數字,字母,下劃線,減號(連字符)
  ...

UUIDField(CharField)           uuid類型

關於choice的注意事項:

  • 在使用選擇標籤時,須要注意choices的選項能夠從數據庫中獲取,可是因爲是靜態字段 獲取的值沒法實時更新,那麼須要自定義構造方法從而達到此目的。

    方式一:

    from app01 import models
    
    class Regform(forms.Form):
        username = forms.CharField(label='用戶名',min_length=6)
        pwd = forms.CharField(label='密碼',widget=forms.PasswordInput)
        gender = forms.ChoiceField(label='性別',choices=((1,'男'),(2,'女')))
        hobby = forms.MultipleChoiceField(
            label='興趣',
            #choices=models.Hobby.objects.values_list('id','name')
        )
    
        def __init__(self,*args,**kwargs):
            super(Regform, self).__init__(*args,**kwargs)
            self.fields['hobby'].choices = models.Hobby.objects.values_list('id','name')
    

    方式二:

        hobby = forms.ModelMultipleChoiceField(
            label='興趣',
            queryset=models.Hobby.objects.all()
        )
    

12.2.13.4校驗

  • 方式一:內置校驗

    required=True
    min_length
    max_length
    
    from django.core.validators import RegexValidator
    
    class Regform(forms.Form):
        
        phone = forms.CharField(
            validators=[RegexValidator(r'^1[3-9]\d{9}$','手機號格式不正確')]
        )
    
  • 2.自定義校驗器

    from django.core.exceptions import ValidationError
    
    def checkname(value):
        #經過校驗規則,不作任何操做
        #未經過校驗規則,拋出異常
        if 'alex' in value:
            raise ValidationError('你的操做錯誤')
    
    class Regform(forms.Form):
        username = forms.CharField(
            label='用戶名',
            min_length=6,
        validators = [checkname],
        )
    

  • 3.鉤子函數:
    1. 局部鉤子

      def clean_字段名(self):
          # 局部鉤子
          # 經過校驗規則  必須返回當前字段的值
          # 不經過校驗規則   拋出異常
          raise ValidationError('不符合社會主義核心價值觀')
      
    2. 全局鉤子

      def clean(self):
          # 全局鉤子
          # 經過校驗規則  必須返回當前全部字段的值
          # 不經過校驗規則   拋出異常   '__all__'
          pass
      

12.2.13.5 is_valid的流程

  • 1.執行full_clean()的方法:

    定義錯誤字典

    定義存放清洗數據的字典

    2.執行_clean_fields方法:

    循環全部的字段

    獲取當前的值

    對進行校驗 ( 內置校驗規則 自定義校驗器)

    1. 經過校驗

      self.cleaned_data[name] = value

      若是有局部鉤子,執行進行校驗:

      經過校驗——》 self.cleaned_data[name] = value

      不經過校驗——》 self._errors 添加當前字段的錯誤 而且 self.cleaned_data中當前字段的值刪除掉

      1. 沒有經過校驗

      self._errors 添加當前字段的錯誤

    3.執行全局鉤子clean方法

12.2.14Django中間件

12.2.14.1中間件介紹

  • 什麼是中間件

    官方的說法:中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局範圍內改變Django的輸入和輸出。每一箇中間件組件都負責作一些特定的功能。

    可是因爲其影響的是全局,因此須要謹慎使用,使用不當會影響性能。

    說的直白一點中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在處理請求的特定的時間去執行這些方法。

  • 中間件樣式

    打開Django項目的Settings.py文件,看到下圖的MIDDLEWARE配置項。

    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',
    ]
    

    MIDDLEWARE配置項是一個列表,列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。

12.2.14.2自定義中間件

中間件能夠定義五個方法,分別是:

  • process_request(self,request)

  • process_view(self, request, view_func, view_args, view_kwargs)

  • process_template_response(self,request,response)

  • process_exception(self, request, exception)

  • process_response(self, request, response)

以上方法的返回值能夠是None或一個HttpResponse對象,若是是None,則繼續按照django定義的規則向後繼續執行,若是是HttpResponse對象,則直接將該對象返回給用戶。

四個特徵 :執行時間,執行順序 ,參數,和返回值

(process理解爲處理的意思)

  • 1.process_request(self,request)

    • 執行時間 :視圖函數以前

    • 參數:

      • request --->和視圖函數中的request是同一個request

    • 執行順序 :

      • 按照註冊的順序,依次執行

    • 返回值

      • None : 正常流程

      • HttpResponse :後面的中間件process_request,視圖函數都不執行,直接執行當前中間件的process_response方法,倒敘執行以前的中間件的process_response方法,響應直接返回給瀏覽器

  • 2.process_response(self, request, response)

    • 執行時間 :視圖函數以後

    • 參數 :

      • request --->和視圖函數中的request是同一個request

      • response--->返回給瀏覽器的響應對象

    • 執行順序 :

      • 按照註冊的順序,倒敘執行

    • 返回值 :

      • Httpresponse :必須返回response對象

  • 3.process_view(self, request, view_func, view_args, view_kwargs)

    • 執行時間 :視圖函數以前,process_request以後

    • 參數 :

      • request --->和視圖函數中的request是同一個request

      • view_func --->視圖函數

      • view_args--->視圖函數中的位置參數

      • view_kwargs--->視圖函數中的關鍵字參數

    • 執行順序 :

      • 按照註冊的順序 執行

    • 返回值 :

      • None : 正常流程

      • HttpResponse :後面中間的process_view,視圖函數都不執行,直接執行最後一個(註冊順序)中間件的process_response方法,倒敘執行以前的中間件的process_response方法,響應直接返回給瀏覽器

         

  • 4.process_exception(self, request, exception) 處理異常

    • 執行時間(觸發條件) :視圖層面有錯才執行

    • 參數 :

      • request --->和視圖函數中的request是同一個request

      • exception --->錯誤對象

    • 執行順序 :

      • 按照註冊的順序,倒敘執行

    • 返回值 :

      • None :交給下一個中間件護理異常,都沒有處理就交給django處理

      • HttpResponse :後面的中間的process_exception都不執行,直接執行最後一箇中間件中的process_response方法,倒敘執行以前的中間件的process_response方法,響應直接返回給瀏覽器

  • 5.process_template_response(self,request,response) (用的比較少)

    • 執行時間(觸發條件) :視圖返回的是一個template_response對象

    • 參數 :

      • request --->和視圖函數中的request是同一個request

      • response--->template_response對象

    • 執行順序 :

      • 按照註冊的順序,倒敘執行

    • 返回值 :

      • 必須返回httpresponse對象

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Md1(MiddlewareMixin):
    def process_request(self,request):
        print('md1 process_request')
        #return HttpResponse('md1')

    def process_response(self,request,response):
        print('md1 process_response')
        return response

    def process_view(self,request,view_func,view_args,view_kwargs):
        print(view_func,view_args,view_kwargs)
        print('md1 process_view')
        # return HttpResponse('md1 process_view')

    def process_exception(self,request,exception):
        print('md1 process_exception')
        # return HttpResponse('錯誤處理完成')

    def process_template_response(self,request,response):
        print('md1 process_template_response')
        return response



class Md2(MiddlewareMixin):
    def process_request(self, request):
        print('md2 process_request')

    def process_response(self,request,response):
        print('md2 process_response')
        return response

    def process_view(self,request,view_func,view_args,view_kwargs):
        # print(view_func,view_args,view_kwargs)
        print('md2 process_view')

    def process_exception(self,request,exception):
        print('md2 process_exception')
        # return HttpResponse('錯誤處理完成')

    def process_template_response(self,request,response):
        response.template_name = 'index2.html'
        response.context_data['name']='pei'
        print('md2 process_template_response')
        return respons

 

12.2.14.3中間件的執行流程

請求到達中間件以後,先按照正序執行每一個註冊中間件的process_reques方法,process_request方法返回的值是None,就依次執行,若是返回的值是HttpResponse對象,再也不執行後面的process_request方法,而是執行當前對應中間件的process_response方法,將HttpResponse對象返回給瀏覽器。也就是說:若是MIDDLEWARE中註冊了6箇中間件,執行過程當中,第3箇中間件返回了一個HttpResponse對象,那麼第4,5,6中間件的process_request和process_response方法都不執行,順序執行3,2,1中間件的process_response方法。

process_request方法都執行完後,匹配路由,找到要執行的視圖函數,先不執行視圖函數,先執行中間件中的process_view方法,process_view方法返回None,繼續按順序執行,全部process_view方法執行完後執行視圖函數。假如中間件3 的process_view方法返回了HttpResponse對象,則4,5,6的process_view以及視圖函數都不執行,直接從最後一箇中間件,也就是中間件6的process_response方法開始倒序執行。

process_template_response和process_exception兩個方法的觸發是有條件的,執行順序也是倒序。總結全部的執行流程以下:

相關文章
相關標籤/搜索