一篇部落格搞定Django基礎

wangfan741發表於2020-10-26

web框架本質

一. scoket 回顧

1.TCP/UDP協議

TCP:一種面向連線的、可靠的、傳輸層通訊協議(比如:打電話);優點:可靠,穩定,傳輸完整穩定,不限制資料大小;缺點:慢,效率低,佔用系統資源高,一發一收都需要對方確認
應用:Web瀏覽器,電子郵件,檔案傳輸,大量資料傳輸的場景

服務端:七步: 建立物件–>繫結ip和埠號–>開啟監聽–>三次握手–>處理收發邏輯–>四次揮手–>退還埠

import socket
#1.建立物件
sk = socket.socket()
#2.繫結ip和埠
sk.bind(("127.0.0.1",9001))
#3.開啟監聽
sk.listen()
#4.三次握手
conn,addr = sk.accept()
#5.處理收發資料邏輯
res = conn.recv(1024)
print(res.decode('utf-8'))
#6.四次揮手
conn.close()
#7.退還埠
sk.close()

客戶端:四步:建立物件–>建立連線–>處理收發訊息邏輯–>關閉連線

import socket
#1.建立物件
sk = socket.socket()
#2.建立連線
sk.connect(('127.0.0.1',9001))
#3.處理收發訊息邏輯
sk.send('你好'.encode())
#4.關閉連線
sk.close()

UDP:一種無連線的,不可靠的傳輸層通訊協議(比如:發簡訊); 優點:速度快,可以多人同時聊天,耗費資源少,不需要建立連線; 缺點:不穩定,不能保證每次資料都能接收到
應用:IP電話,實時視訊會議,聊天軟體,少量資料傳輸的場景

服務端:四步:建立物件–>繫結ip和埠–>收發訊息(一開始只能收)–>關閉連線

import socket
#1.建立物件
sk = socket.socket(type=socket.SOCK_DGRAM)
#2.繫結ip和埠
sk.bind(('127.0.0.1',9002))
#3.收發訊息
msg,cli_addr = sk.recvfrom(1024)
print(msg.decode())
sk.sendto('你好'.encode(),cli_addr)
#4.關閉連線
sk.close()

客戶端:三步:建立物件–>發收訊息–>關閉連線

import socket
#1.建立物件
sk = socket.socket(type=socket.SOCK_DGRAM)
#2.收發訊息
sk.sendto('hello'.encode(),('127.0.0.1',9002))
msg,server_addr = sk.recvfrom(1024)
print(msg.decode())
#3.關閉連線
sk.close()

二.HTTP協議

HTTP是一個客戶端終端(使用者)和伺服器端(網站)請求和應答的標準 .

1.http工作原理

  1. 客戶端連線到Web伺服器
  2. 傳送HTTP請求
  3. 伺服器接受請求並返回HTTP響應
  4. 釋放連線TCP連線
  5. 客戶端瀏覽器解析HTML內容

2.請求方法和響應方法

請求:

在這裡插入圖片描述
​ 請求方法:GET方法:讀取資料 POST方法…

響應:

在這裡插入圖片描述
​ 狀態碼:1xx~4xx:

​ 1xx訊息——請求已被伺服器接收,繼續處理

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

​ 3xx重定向——需要後續操作才能完成這一請求

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

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

三.自定義的web框架

自定義的web框架基於HTTP1.1和TCP主要是處理收發邏輯和如何連線到資料庫.

1.多函式的自定義web框架

分析: 輸入127.0.0.1:9000 —> 瀏覽器傳送get請求 —> get 路徑 HTTP1.1\r\n… —> 服務端答應 —> HTTP/1.1 200 ok\r\n\r\n 響應正文

import socket,time
#######socket---tcp連線#########################################
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
#######回訊息的函式###############################################
def html(coon):     #動態頁面
    cur_time = time.strftime("%Y-%m-%d %H:%M:%S") #是字串型別,如果是=時間戳是浮點型
    with open('home.html',mode='r',encoding='utf-8') as f:
        data = f.read()
    data = data.replace("xxoo",cur_time).encode('utf-8')
    conn.send(data)
    conn.close()

def ico(conn):
    with open('logo.ico','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def css(conn):
    with open('test.css','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def jpg(conn):
    with open('bg.jpg','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def login(conn):
    with open('login.html','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def js(conn):
    with open('test.js','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def zhuce(conn):
    with open('zhuce.html','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

urlpatterns = [
    ('/',html),
    ('/logo.ico',ico),
    ('/test.css',css),
    ('/bg.jpg',jpg),
    ('/login.html',login),
    ('/test.js',js),
    ('/zhuce.html',zhuce)
]

while True:
    conn,addr = sk.accept()
    from_browser_msg = conn.recv(1024)
    print(from_browser_msg)
    #從請求中獲取路徑
    path = from_browser_msg.decode().split(' ')[1]
    #傳送響應的狀態行
    conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
    #傳送響應正文
    for i in urlpatterns:
        if path == i[0]:
            i[1](conn)

sk.close()

home.html檔案:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" href="logo.ico">
    <link rel="stylesheet" href="test.css">
</head>
<body class="c1">
    <h1 style="color: red ">歡迎來到祖安</h1>
    <h2 style="color: red ">你訪問的時間是xxoo</h2>
    <button id="d1">登入</button>
    <a href="http://127.0.0.1:9000/zhuce.html" style="color: yellow">註冊</a>
</body>
<script src="test.js"></script>
</html>

test.css檔案:

.c1{
    background: url('bg.jpg') no-repeat ;
    background-size:100%,100%;
}

test.js檔案:

var d = document.getElementById('d1')
d.onclick = function () {
    window.location.href="http://127.0.0.1:9000/login.html"
}

login.html檔案:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:9000/login" method="post">
    使用者名稱<input type="text">
    密碼 <input type="password">
    <input type="submit">
</form>
</body>
</html>

zhuce.html檔案:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 style="color: red">不會吧不會吧,不會真有人想當祖安人吧</h1>
</body>
</html>

2.wsgiref模組定義的web框架

**分析:**wsgiref模組封裝了socket模組,用法如下:

from wsgiref.simple_server import make_server
def application(environ, start_response):
     '''
    environ: 是全部加工好的請求資訊,加工成了一個字典,通過字典取值的方式就能拿到很多你想要拿到的資訊
    start_response: 幫你封裝響應資訊的(響應行和響應頭)
    :return: 需要傳送的資料
    '''
    path = environ['PATH_INFO']  #獲取路徑
    method = environ['REQUEST_METHOD'] #獲取請求方法
    content_length = int(environ.get('CONTENT_LENGTH',0)) #獲取請求資料的長度
    request_data = environ['wsgi.input'].read(content_length) #post請求讀取請求資料的方式 結果為位元組流.decode()
    '''
    寫邏輯
    data = ...
    '''
    start_response('200 OK', [('a','1'),])
    return [data]
httpd = make_server('127.0.0.1', 8001, application) #類似socketserver
httpd.serve_forever() #監聽請求

**補充:**from urllib.parse import parse_qs , parse_qs方法用來解析資料,用法如下:

from urllib.parse import parse_qs
print('請求資料為', data) #請求資料為 username=asdf&password=asdf
data = parse_qs(data)
print('格式化之後的資料',data) #格式化之後的資料 {'username': ['asdf'], 'password': ['asdf']}
uname = data.get('username') #通過字典可以直接取出
pwd = data.get('password')

基於上述兩個模組可以改造上面的web框架並連線到資料庫,一個完整的web框架就完成了

主程式:

import time
from urllib.parse import parse_qs                   #parse : url的解析,合併,編碼,解碼
from wsgiref.simple_server import make_server       #按規範封裝了socket
from auth import check

def html(environ):
    cur_time = time.strftime("%Y-%m-%d %H:%M:%S")          #是字串型別,如果是=時間戳是浮點型
    with open('home.html',mode='r',encoding='utf-8') as f:
        data = f.read()
    data = data.replace("xxoo",cur_time).encode('utf-8')
    return data

def ico(environ):
    with open('logo.ico','rb') as f :
        data = f.read()
    return data

def css(environ):
    with open('test.css','rb') as f :
        data = f.read()
    return data

def jpg(environ):
    with open('bg.jpg','rb') as f :
        data = f.read()
    return data

def login(environ):
    if environ['REQUEST_METHOD'] == 'GET':
        with open('login.html','rb') as f :
            data = f.read()
        return data
    else:
        content_length = int(environ.get('CONTENT_LENGTH', 0))
        data = environ['wsgi.input'].read(content_length).decode('utf-8')
        data = parse_qs(data)
        uname = data.get('username')
        pwd = data.get('password')
        staut = check(uname,pwd)
        if staut:
            with open('login_suc.html','rb') as f :
                data = f.read()
                return data
        else:
            return '登入失敗'.encode('gbk')

def js(environ):
    with open('test.js','rb') as f :
        data = f.read()
    return data

def zhuce(environ):
    with open('zhuce.html','rb') as f :
        data = f.read()
    return data

urlpatterns = [
    ('/',html),
    ('/logo.ico',ico),
    ('/test.css',css),
    ('/bg.jpg',jpg),
    ('/login.html',login),
    ('/test.js',js),
    ('/zhuce.html',zhuce),
    ('/login',login)
]

def application(environ,start_response):
    path = environ['PATH_INFO']
    start_response('200 OK',[('a','1'),])
    for i in urlpatterns:
        if i[0] == path:
            data = i[1](environ)
            break
    else:
        data = b'404 not found'
    return [data]
httpd = make_server('192.168.13.26',9000,application)
httpd.serve_forever()

auth.py 連線資料庫程式:

import pymysql
def check(uname,pwd):
    conn = pymysql.connect(host='127.0.0.1',user='root',password='',database='db0913',port=3306)
    cursor = conn.cursor()
    sql = 'select * from t1 where id = %s and name = %s'
    res = cursor.execute(sql,(uname,pwd))
    return res

login_suc.html 登入成功介面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 style="color: yellow">寶貝兒,恭喜你登陸成功</h1>
</body>
</html>

3.jinja2 模板渲染

用於動態介面,原理字串替換
傳送門

4.MVC和MTV框架

MVC模式:

模型(M) ,控制器© ,檢視(V) ,
在這裡插入圖片描述
MTV模式:

  • M 代表模型(Model): 負責業務物件和資料庫的關係對映(ORM)。

  • T 代表模板 (Template):負責如何把頁面展示給使用者(html)。

  • V 代表檢視(View): 負責業務邏輯,並在適當時候呼叫Model和Template

    還需要一個URL分發器,它的作用是將一個個URL的頁面請求分發給不同的View處理,View再呼叫相應的Model和Template
    在這裡插入圖片描述

5.Django安裝

pip3 install django==1.11.9

Django框架

一.url和views

1.用函式寫views.py(FBV)

urls.py寫法:1.分為命名分組和無命名分組,2.與views中函式一一對應.具體如下:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^book/(?P<year>\d+)/(?P<month>\d+)',views.book),
    url(r'^li1/(\d+)/(\d+)',views.li1)
]

**views.py寫法:**1.第一個引數是request物件,2.返回的三種方式:HttpResponse,render,redirect.具體如下:

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
def book(request,year,month):
    '''
    request.GET   #包含所有HTTP  GET引數的類字典物件 http://127.0.0.1:8000/index/?xx=oo -- <QueryDict: {'xx': ['oo']}>
	request.POST  #包含所有HTTP POST引數的類字典物件 <QueryDict: {'uname': ['asf'], 'pwd': ['asdf']}>
	request.method #請求方法
	request.path   #請求路徑
	request.path_info  #返回使用者訪問url,不包括域名
	request.get_full_path() # /index/?xx=oo  路徑+查詢引數
	request.body # b'uname=asf&pwd=asdf'
    '''
    #return HttpResponse('XXX')  #返回xxx
    #return render(request,'book.html',{'title':'xx'}) #返回html頁面
    return redirect('/li1/1/2')  #重定向
def li1(request,n,m):
    return HttpResponse('li1'+str(m)+str(n))

2.用類寫views.py(CBV)

urls.py寫法:用views.類.as_view().具體如下:

url(r'^book1/(\d+)/(\d+)',views.Book.as_view()),

views.py寫法:繼承View.具體如下:

from django.views import View
class Book(View):
    def get(self,request,year,month):
        return HttpResponse(year+month+'書籍')
    def post(self,requset,year,month):
        pass

用類寫views.py檔案,可以將請求方法分發到同名的類方法去處理---->繼承了dispatch方法,其原始碼如下,很明顯,用了反射,將請求方法分發到同名的類方法去處理

    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

擴充dispatch方法:用類寫views.py檔案時,先觸發dispatch方法–>反射–>觸發get方法–>get方法返回值–>dispatch方法接收返回.具體程式碼如下:

views.py檔案:

class Book2(View):
    def dispatch(self,request, *args, **kwargs):
        print('dispatch方法觸發')
        ret = super().dispatch(request,*args,**kwargs)
        print('dispatch方法結束')
        return ret
    def get(self,request,year,month):
        print('get方法觸發')
        return HttpResponse(year+month+'書籍')
    def post(self,requset,year,month):
        pass

urls.py檔案:

url(r'^book2/(\d+)/(\d+)',views.Book2.as_view())

3.命名URL(別名)和URL的反向解析

起別名:

url(r'^home', views.home, name='home'),  # 給我的url匹配模式起名(別名)為 home,別名不需要改,路徑你就可以隨便改了,別的地方使用這個路徑,就用別名來搞
url(r'^index/(\d*)', views.index, name='index'),  # 給我的url匹配模式起名為index

反向解析:

在html模板中:

{% url 'home' %}  #模板渲染的時候,被django解析成了這個名字對應的那個url,這個過程叫做反向解析

在views檔案中:

from django.urls import reverse
reverse("index")

4.url分發和名稱空間模式

url分發:

專案的urls.py寫法:

from django.conf.urls import url,include    #include做路徑分發
from django.contrib import admin
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
]

app01 urls.py寫法:

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^index/', views.index,name='index'),
]

app02 urls.py寫法:

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

名稱空間模式:

當不同的應用中路徑的別名相同時,在進行url別名反向解析時,得到的結果都是settings檔案中最後一個應用的路徑.

解決辦法:在include中加上namespace='app01’即為:include(‘app01.urls’ namespace=‘app01’)—>把app01下的所有路徑劃分了一個單獨的空間.

專案的urls.py寫法:

from django.conf.urls import url,include   
from django.contrib import admin
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    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
urlpatterns = [
    url(r'^index/', views.index,name='index'),
]

app02 urls.py寫法:

from django.conf.urls import url
from app02 import views
# app_name = 'app02'
urlpatterns = [
    url(r'^index/', views.index,name='index'),
]

在views中函式中:

v = reverse('app01:index')

在html檔案中:

{% url 'app01:index'%}

這樣即使app中URL的命名相同,也可以反向解析出正確的URL.

二.模板渲染

1.基本語法

Django內建模板渲染方法:1.return render(request,‘html路徑’,要替換的字典),2.{{}}.具體程式碼如下:

home.html檔案:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>歡迎{{ username }}</h1>
<p>{{ num }}</p>
<p>{{ l1.1 }}</p> <!--列表可以用索引取值-->
<p>{{ d1.k1 }}</p> <!--字典可以通過健取值-->
<p>{{ a.xx }}</p>  <!--物件.屬性-->
<p>{{ a.get_name }}</p>  <!-- 呼叫方法時,只能呼叫無引數的方法 -->
</body>
</html>

views.py檔案:

def home(request):
    username = '123'
    num = 100
    l1 = [11,22,33]
    d1 = {'k1':'v1','k2':'v2'}
    class A():
        def __init__(self):
            self.xx = 'oo'
        def get_name(self):
            return self.xx+'123'
    a = A()
    dic = {
        'username':username,
        'num':num,
        'l1':l1,
        'd1':d1,
        'a':a,
    }
    return render(request,'xx/home.html',dic)

urls.py檔案:

url(r'^home',views.home)

2.過濾器

home.html檔案

<h1>過濾器</h1>
<p>{{ xxxx|default:'abc' }}</p> <!--沒有xxxx時,預設用預設值代替-->
<p>{{ username|length }}</p> <!--用值的長度替換-->
<p>{{ file_size|filesizeformat }}</p> <!--展示為可讀的大小,自動計算單位-->
<p>{{ username|slice:'0:2' }}</p> <!--切片-->
<p>{{ current_time|date:'Y-m-d H:i:s' }}</p> <!--時間格式化-->
<p>{{ a_tag|safe }}</p> <!--將標籤字串識別為一個標籤效果,防xss注入-->
<p>{{ username|truncatechars:4 }}</p> <!--字元截斷-->
<p>{{ username|truncatewords:2 }}</p> <!--單詞截斷-->
<p>{{ username|cut:' ' }}</p> <!--移除字串-->
<p>{{ l1|join:'+' }}</p> <!--字串拼接-->

views.py檔案:

import datetime
def home(request):
    username = '123 456 789'
    num = 100
    l1 = [11,22,33]
    d1 = {'k1':'v1','k2':'v2'}
    class A():
        def __init__(self):
            self.xx = 'oo'
        def get_name(self):
            return self.xx+'123'
    a = A()
    file_size = 10230401
    current_time = datetime.datetime.now()
    a_tag = '<a href="">a標籤</a>'
    dic = {
        'username':username,
        'num':num,
        'l1':l1,
        'd1':d1,
        'a':a,
        'file_size':file_size,
        'current_time':current_time,
        'a_tag':a_tag,
    }
    return render(request,'xx/home.html',dic)

三.標籤

基本語法:{%標籤邏輯%}

1.for迴圈標籤

迴圈列表:

index.html檔案:

<ul>
    {% for i in l1 %}
        <li>{{ i }}</li>
        {% empty %}
        <span></span>
    {% endfor %}
</ul>

views.py檔案:

def index(request):
    l1 = [11,22,33]
    d1 = {'k1':'v1','k2':'v2'}
    return render(request,'index.html',{'l1':l1,'d1':d1})

迴圈字典:

index.html檔案:

<ul>
    {% for k,v in d1.items %}
    <li>{{ k }}--{{ v }}</li>
    {% endfor %}
</ul>

forloop物件:

forloop.counter            當前迴圈的索引值(從1開始),forloop是迴圈器,通過點來使用功能
forloop.counter0           當前迴圈的索引值(從0開始)
forloop.revcounter         當前迴圈的倒序索引值(從1開始)
forloop.revcounter0        當前迴圈的倒序索引值(從0開始)
forloop.first              當前迴圈是不是第一次迴圈(布林值)
forloop.last               當前迴圈是不是最後一次迴圈(布林值)
forloop.parentloop         本層迴圈的外層迴圈的物件,再通過上面的幾個屬性來顯示外層迴圈的計數等

2.if迴圈標籤

index.html檔案

{% if num == 100 %}
    <h1>123</h1>
{% endif %}

3.for巢狀+if

index.html檔案:

<ul>
    {% for i in l1 %}
        {% if forloop.last %}
            {% for j in i %}
                <li>{{ j }}--{{ forloop.counter }}--{{ forloop.parentloop.counter }}</li>
            {% endfor %}
        {% endif %}
    {% endfor %}
</ul>

views.py檔案:

def index(request):
    l1 = [11,22,33,[1,2,3]]
    d1 = {'k1':'v1','k2':'v2'}
    num = 100
    return render(request,'index.html',{'l1':l1,'d1':d1,'num':num})

四.元件

基本語法:在html檔案中加{% include ‘zujian.html’ %}可引入一個介面.具體程式碼如下:

index.html檔案

{% include 'zujian.html' %}

zujian.html檔案:

<h1>導航欄</h1>

五.靜態檔案配置

方式1:

從根目錄直接引入:

html檔案:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/xx.css">
</head>
<body>
<div class="c1">hello</div>
<div class="c2"></div>
</body>
<script src="/static/xx.js"></script>
</html>

setting檔案:

STATIC_URL = '/static/' #別名
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'xxxx') #資料夾名
]

css檔案:

.c1{
    color: red;
}
.c2{
    background-color: green;
    height: 200px;
    width: 200px;
}

js檔案:

var divEle = document.getElementsByClassName('c2')[0];
divEle.onclick = function () {
    this.style.backgroundColor = 'red';
};

方式2:

用標籤配置:1.引入{% load static %},2.,

html檔案:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
{#    <link rel="stylesheet" href="/static/xx.css">#}
    <link rel="stylesheet" href="{% static 'xx.css' %}">
</head>
<body>
<div class="c1">hello</div>
<div class="c2"></div>
</body>
{#<script src="/static/xx.js"></script>#}
<script src="{% static 'xx.js' %}"></script>
</html>

六.ORM(運算元據庫)

ORM,object relational mapping,物件關係對映,做資料庫操作的.

1.操作Django自帶資料庫

app01/models.py:

class Book(models.Model):
    # id = models.AutoField(primary_key=True) # id int primary key auto_increment
    title = models.CharField(max_length=32) #對應sql語句:title varchar(32)
    price = models.DecimalField(max_digits=5,decimal_places=2) #price decimal(5,2)
    pub_date = models.DateField() #pub_date date
    publish = models.CharField(max_length=32)

執行資料庫同步指令,在專案根目錄下面執行

python manage.py makemigrations
python manage.py migrate

檢視FIeld和mysql中的欄位關係對比

    _data_types = {
        'AutoField': 'integer AUTO_INCREMENT',
        'BigAutoField': 'bigint AUTO_INCREMENT',
        'BinaryField': 'longblob',
        'BooleanField': 'bool',
        'CharField': 'varchar(%(max_length)s)',
        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
        'DateField': 'date',
        'DateTimeField': 'datetime',
        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
        'DurationField': 'bigint',
        'FileField': 'varchar(%(max_length)s)',
        'FilePathField': 'varchar(%(max_length)s)',
        'FloatField': 'double precision',
        'IntegerField': 'integer',
        'BigIntegerField': 'bigint',
        'IPAddressField': 'char(15)',
        'GenericIPAddressField': 'char(39)',
        'NullBooleanField': 'bool',
        'OneToOneField': 'integer',
        'PositiveIntegerField': 'integer UNSIGNED',
        'PositiveSmallIntegerField': 'smallint UNSIGNED',
        'SlugField': 'varchar(%(max_length)s)',
        'SmallIntegerField': 'smallint',
        'TextField': 'longtext',
        'TimeField': 'time',
        'UUIDField': 'char(32)',
    }

增加

增加有三中方式:1.通過新增屬性(obj=models.Book();obj.save()),2.通過create方法(obj= models.Book.objects.create()),3.models.Book.objects.bulk_create(obj_list)批量新增

from app01 import models
import datetime
# Create your views here.
def book(request):
    #方式一
    '''
    obj = models.Book(
        title='金瓶梅後傳',
        price=2.8,
        # pub_date='2000-08-12',  #這樣的格式字串
        pub_date=datetime.datetime.now(),  # 時間日期型別
        publish='31期紅浪漫出版社',
    )
    obj.save()
    '''
    #方式二
    '''
    obj= models.Book.objects.create(
        # obj是當前建立的新的記錄物件
        title='金瓶梅前傳',
        price=9.9,
        # pub_date='2000-08-12',  #這樣的格式字串
        pub_date=datetime.datetime.now(),  # 時間日期型別
        publish='31期夕陽紅出版社',
    )
    '''
    #批量新增
    obj_list = []
    for i in range(1,4):
        obj = models.Book(
            title='少婦白潔' + str(i),
            price=i,
            pub_date=f'2000-08-1{i}',
            publish='31期夕陽紅出版社',
        )
        obj_list.append(obj)
    models.Book.objects.bulk_create(obj_list)
    return HttpResponse('ok')

2.操作mysql資料庫

1.連線mysql資料庫:

1.配置settings.py檔案,2.配置主目錄下的init.py檔案

settings.py檔案:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db0917',
        'HOST': '127.0.0.1',
        'POST': 3306,
        'USER': 'root',
        'PASSWORD': '123456'
    }
}

init.py檔案:

import pymysql
pymysql.install_as_MySQLdb()

2.建立一張表

1.寫models檔案,2.在專案根目錄下執行兩條命令

models.py檔案:

class T1(models.Model):
    # id = models.AutoField(primary_key=True) # id int primary key auto_increment
    title = models.CharField(max_length=32) #對應sql語句:title varchar(32)
    price = models.DecimalField(max_digits=5,decimal_places=2) #price decimal(5,2)
    pub_date = models.DateField() #pub_date date
    publish = models.CharField(max_length=32)

命令視窗執行:

python manage.py makemigrations
python manage.py migrate

3.增

增加有三中方式:1.通過新增屬性(obj=models.T1();obj.save()),2.通過create方法(obj= models.T1.objects.create()),3.models.T1.objects.bulk_create(obj_list)批量新增

views.py檔案:

from app01 import models
import datetime
# Create your views here.
def book(request):
    #方式一
    obj = models.T1(
        title='金瓶梅後傳',
        price=2.8,
        # pub_date='2000-08-12',  #這樣的格式字串
        pub_date=datetime.datetime.now(),  # 時間日期型別
        publish='31期紅浪漫出版社',
    )
    obj.save()
    
    #方式二
	'''
    obj= models.T1.objects.create(
        # obj是當前建立的新的記錄物件
        title='金瓶梅前傳',
        price=9.9,
        # pub_date='2000-08-12',  #這樣的格式字串
        pub_date=datetime.datetime.now(),  # 時間日期型別
        publish='31期夕陽紅出版社',
    )
    '''
    
    #批量新增
    '''
    obj_list = []
    for i in range(1,4):
        obj = models.T1(
            title='少婦白潔' + str(i),
            price=i,
            pub_date=f'2000-08-1{i}',
            publish='31期夕陽紅出版社',
        )
        obj_list.append(obj)
    models.T1.objects.bulk_create(obj_list)
    '''
    return HttpResponse('ok')

update_or_create:有就更新,沒有就建立 ,還有個get_or_create,有就查詢出來,沒有就建立

4.查

13種方法:all() , filter(條件) , get(條件) , exclude(條件) , order_by() , reverse(),count(),first(),last(),exists(),values(),values_list(),distinct()

	ret = models.T1.objects.all()   #queryset型別
    ret = models.T1.objects.filter(title='少婦白潔1') #queryset型別
    ret = models.T1.objects.get(title='少婦白潔1') #獲取有且只有一條資料,找不到/超過一條都會報錯
    ret = models.T1.objects.exclude(id=5) #queryset型別,返回id不等於6的所有物件,也可以在queryset型別基礎上執行
    ret = models.T1.objects.all().order_by('price','id') #queryset型別,按照price進行升序,price相同的資料,按照id進行升序
    ret = models.T1.objects.all().order_by('-price') #按price降序排列
    ret = models.T1.objects.all().order_by('price').reverse()#對查詢結果反向排序
    ret = models.T1.objects.all().count() #對queryset型別資料呼叫,返回查詢到的物件數量
    ret = models.T1.objects.all().first() #model物件,返回第一條記錄
    ret = models.T1.objects.all()[0]    #同first,不支援負索引
    ret = models.T1.objects.all().last() #返回最後一條記錄
    ret = models.T1.objects.all().exists() #QuerySet包含資料,就返回True,否則返回False
    ret = models.T1.objects.values('title','price') #querset型別,<QuerySet [{'title': '少婦白潔1', 'price': Decimal('1.00')}...
    ret = models.T1.objects.all().values('title','price') #還可以通過queryset型別資料來呼叫
    ret = models.T1.objects.all().values_list('title','price')#用法同values,返回的時元組
    ret = models.T1.objects.all().values_list('price').distinct() #values和values_list得到的queryset型別的資料來呼叫,從返回結果中剔除重複紀錄
    print(ret)

filter基於雙下劃線的模糊查詢

__year , __day , __startswith , __istartswith , __endswith , __icontains , __in , __range , __gt , __gte , __lt , __lte
按日期查詢
ret = models.Book.objects.filter(pub_date__year='2000', pub_date__month='8',pub_date__day='12')
    print(ret) 
ret = models.Book.objects.filter(title__startswith='少婦')
ret = models.Book.objects.filter(title__istartswith='py')
ret = models.Book.objects.filter(title__endswith='2')
ret = models.Book.objects.filter(title_icontains='python')
不區分大小寫
ret = models.Book.objects.filter(title__icontains='python')
print(ret)

ret = models.Book.objects.filter(price__in=[3,4])  # or 等於3或者等於4
ret = models.Book.objects.filter(price__range=[1,3]) #between 1 and 3
print(ret)

ret = models.Book.objects.filter(pub_date__year=2018)
ret = models.Book.objects.filter(pub_date__year='2018')    

# 價格大於3的
ret = models.Book.objects.filter(price__gt=3)
# 價格大於等於3的
ret = models.Book.objects.filter(price__gte=3)
# 價格小於3的
ret = models.Book.objects.filter(price__lt=3)
# 價格小於等於3的
ret = models.Book.objects.filter(price__lte=3)

5.刪

.delete()

 models.Book.objects.filter(title='少婦白潔1').delete()
 models.Book.objects.get(id=3).delete()   
query型別資料和模型類物件都可以呼叫delete方法來進行刪除

6.改

兩種方式:1 .update() 2.物件.屬性修改

    # 修改
    # 方式1
    # models.Book.objects.filter(id=4).update(
    #     title='少婦白潔1',
    #     price=4,
    # )
    # 方式2
    obj = models.Book.objects.get(id=6)
    obj.price = 18
    obj.save()
    
    obj.update()  模型類物件不能直接使用update方法

3.多表操作

1.建立多表

ad = models.OneToOneField(to=“AuthorDetail”, to_field=‘id’, on_delete=models.CASCADE)–>級聯,一對一

authors=models.ManyToManyField(to=‘Author’,)–>多對多預設建立第三張表

from django.db import models

# Create your models here.
# 作者表
class Author(models.Model): #比較常用的資訊放到這個表裡面
    name=models.CharField( max_length=32)
    age=models.IntegerField()  #int
    # 與AuthorDetail建立一對一的關係,一對一的這個關係欄位寫在兩個表的任意一個表裡面都可以
    ad = models.OneToOneField(to="AuthorDetail", to_field='id', on_delete=models.CASCADE)
    # authordetail_id
    # foreign key + unique


# 作者詳細資訊表
class AuthorDetail(models.Model):#不常用的放到這個表裡面
    birthday=models.DateField()
    telephone=models.CharField(max_length=11)
    addr=models.CharField(max_length=64)


# 出版社表
class Publish(models.Model):
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)


# 書籍表
class Book(models.Model):
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    # publish=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
    publish=models.ForeignKey(to="Publish") #預設級聯刪除,預設關聯的是另外一張表的id欄位
    authors=models.ManyToManyField(to='Author',) #自動建立第三張表,id author_id book_id,不會作為本表的欄位出現

2.建立欄位時的一些引數

(1)null
如果為True,Django 將用NULL 來在資料庫中儲存空值。 預設值是 False.
 
(1)blank
如果為True,該欄位允許不填。預設為False。
要注意,這與 null 不同。null純粹是資料庫範疇的,而 blank 是資料驗證範疇的。
如果一個欄位的blank=True,表單的驗證將允許該欄位是空值。如果欄位的blank=False,該欄位就是必填的。
 
(2)default
欄位的預設值。可以是一個值或者可呼叫物件。如果可呼叫 ,每有新物件被建立它都會被呼叫,如果你的欄位沒有設定可以為空,那麼將來如果我們後新增一個欄位,這個欄位就要給一個default值
 
(3)primary_key
如果為True,那麼這個欄位就是模型的主鍵。如果你沒有指定任何一個欄位的primary_key=True,
Django 就會自動新增一個IntegerField欄位做為主鍵,所以除非你想覆蓋預設的主鍵行為,
否則沒必要設定任何一個欄位的primary_key=True(4)unique
如果該值設定為 True, 這個資料欄位的值在整張表中必須是唯一的
 
(5)choices
由二元組組成的一個可迭代物件(例如,列表或元組),用來給欄位提供選擇項。 如果設定了choices ,預設的表單將是一個選擇框而不是標準的文字框,<br>而且這個選擇框的選項就是choices 中的選項。

(6)db_index
  如果db_index=True 則代表著為此欄位設定資料庫索引。


DatetimeField、DateField、TimeField這個三個時間欄位,都可以設定如下屬性。
(7)auto_now_add
    配置auto_now_add=True,建立資料記錄的時候會把當前時間新增到資料庫。

(8)auto_now
    配置上auto_now=True,每次更新資料記錄的時候會更新該欄位,標識這條記錄最後一次的修改時間。

特別說明:

choices屬性:

sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))

資料庫裡面存的是1或者2
通過model模型類物件.get_屬性名稱_display()可以獲取到數字對應的文字內容

auto_now_add和auto_now引數:

class t1(models.Model):
    # defauit 在使用orm操作新增資料時生效.
    name = models.CharField(max_length=12, default='張三')
    sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))

    d1 = models.DateTimeField(auto_now_add=True,null=True)  #自動新增建立記錄的時間
    d2 = models.DateTimeField(auto_now=True,null=True) #自動新增建立記錄的時間,更新記錄是也能自動更新為最新時間

auto_now 自動更新時間只有在save更新時才生效,update不生效
所以如果要做更新時間的處理,那麼最好手動獲取當前時間並修改

3.增和刪

    增加
    一對一
    models.AuthorDetail.objects.create(
        birthday='2018-01-01',
        telephone='13800000000',
        addr='北京'
    )
    ad_obj = models.AuthorDetail.objects.get(id=1)
    models.Author.objects.create(
        name='明皓',
        age=38,
        # ad=ad_obj,
        ad_id=2,
    )
    ad_obj = models.AuthorDetail.objects.get(id=4)
    obj= models.Author(
        name='楊浩',
        age=47,
        ad=ad_obj,
        # ad_id=3,
    )
    obj.save()

    一對多
    models.Book.objects.create(
        title='金瓶梅第二部',
        publishDate='2018-08-08',
        price=22,
        # publishs=models.Publish.objects.get(id=1),
        publishs_id=1,

    )

    多對多
    obj = models.Book.objects.filter(id=1).first()
    a1 = models.Author.objects.get(id=1)
    a2 = models.Author.objects.get(id=2)
    obj.authors.add(a1,a2)
    obj.authors.add(1,2)
    obj.authors.add(*[1,2])

    刪除和更新
    一對一和一對多 ,基本和單表一樣(級聯刪除)
    models.Author.objects.get(id=1).delete()
    models.AuthorDetail.objects.get(id=2).delete()
    models.Book.objects.get(id=1).delete()

    多對多刪除
    ret = models.Book.objects.get(id=2)
    ret.authors.clear()  # 清空該書籍對應的第三張表中的記錄
    ret.authors.remove(3,4)  #指定刪除該書和哪些作者的關係

4.改

4.1一對一/一對多修改

兩種方式:1.用欄位名稱進行修改(ad_id=1),2.通過物件.屬性進行修改.具體程式碼如下:

models.Author.objects.filter(name='精華').update(
    name='敬華',
    age=50,
    # ad=models.AuthorDetail.objects.get(id=1),
    ad_id=1,
)
4.2多對多修改

set方法

obj = models.Book.objects.get(id=2)
obj.authors.set(['3',]) # 值是字串,clear + add

5.檢視和執行原生sql

5.1檢視

方式一:

在python檔案中執行如下語句:

from django.db import connection  #通過這種方式也能檢視執行的sql語句
# print(connection.queries)

方式二:

在settings.py檔案中配置如下內容:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}
5.2執行原生sql語句

方式一:from django.db import connection

from django.db import connection
cursor = connection.cursor()
cursor.execute('select * from app01_book;')
print(cursor.fetchall())

方式二:pymysql

方式三:

ret = models.Author.objects.raw('select * from app01_author;')  #只限於本表操作
print(ret)
for i in ret:
    print(i.name)

6.查

6.1一對一

正向查詢:

# 檢視敬華作者的家庭住址
# select app01_authordetail.addr from app01_author inner join app01_authordetail on app01_author.ad_id app01_atuhordetail.id
# where app01_author.name='敬華'
obj = models.Author.objects.get(name='敬華')
print(obj.ad.addr)   #北京

反向查詢:

obj = models.AuthorDetail.objects.get(addr='北京協和')
print(obj.author.name)
6.2 一對多

正向查詢:

查詢金鱗豈是池中物這本書的出版社
正向查詢
obj = models.Book.objects.get(title='金鱗豈是池中物')
print(obj.publishs.name)

反向查詢:

查詢馬哥出版社出版的書籍有哪些
反向查詢
ret = models.Publish.objects.get(name='馬哥出版社')
books = ret.book_set.all()  #<QuerySet [<Book: Book object>, <Book: Book object>]>
print(books.values('title'))
6.3 多對多

正向查詢:

# 查詢金瓶梅第二部是誰寫的
# 正向查詢
# obj = models.Book.objects.get(title='金瓶梅第二部')
# print(obj.authors.all().values('name'))

反向查詢:

# 查詢敬華寫了哪些書
obj = models.Author.objects.get(name='敬華')
print(obj.book_set.all().values('title')) 
6.4基於雙下劃線的跨表查詢
一對一
    檢視敬華作者的家庭住址
    正向寫法
    ret =  models.Author.objects.filter(name='敬華').values('ad__addr')
    反向寫法
    ret = models.AuthorDetail.objects.filter(author__name='敬華').values('addr')
    print(ret) #<QuerySet [{'addr': '北京'}]>

一對多
    查詢金鱗豈是池中物這本書的出版社
    ret = models.Book.objects.filter(title='金鱗豈是池中物').values('publishs__name')
    print(ret) #<QuerySet [{'publishs__name': '小馬哥出版社'}]>
    ret = models.Publish.objects.filter(book__title='金鱗豈是池中物').values('name')
    print(ret) #<QuerySet [{'name': '小馬哥出版社'}]>

多對多
    查詢金瓶梅第二部是誰寫的
    ret = models.Book.objects.filter(title='金瓶梅第二部').values('authors__name')
    print(ret)
    ret = models.Author.objects.filter(book__title='金瓶梅第二部').values('name')
    print(ret) #<QuerySet [{'name': '敬華'}, {'name': '楊浩'}]>
6.5聚合查詢
from django.db.models import Avg,Max,Min,Count,Sum
# 聚合查詢
ret = models.Book.objects.all().aggregate(Avg('price'))
ret = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price'))
print(ret,type(ret)) #{'price__avg': 15.0} <class 'dict'>
#注意結果為字典型別.
6.6分組查詢

sql_mode模式

https://www.cnblogs.com/clschao/articles/9962347.html

# # 分組查詢
# 統計一下每個出版社出版書的平均價格
'''
select publishs_id,avg(price) from app01_book group by publishs_id;
select avg(app01_book.price) from app01_book inner join app01_publish on 
app01_book.publishs_id = app01_publish.id group by app01_publish.name;
'''
ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
print(ret)
ret = models.Publish.objects.annotate(a=Avg('book__price'))
print(ret.values('a','name'))
print(ret)
# <QuerySet [<Publish: Publish object>, <Publish: Publish object>]>
print(ret.values('a','name'))
# sql_mode
# only_full_group_by
6.7 F查詢
#查詢一下點贊數大於評論數的書籍
#針對本表不同欄位資料進行對比時或者本表字典做一些統一修改時使用F查詢
models.Book.objects.filter(dianzan__gt=comment)
ret = models.Book.objects.all()
book_list = []
for i in ret:
     if i.dianzan > i.comment:
        book_list.append(i)

#使用F查詢   
from django.db.models import F
# 點贊數大於評論數的  
ret = models.Book.objects.filter(dianzan__gt=F('comment'))
print(ret)
# 所有書籍上調10塊
models.Book.objects.all().update(price=F('price')+10) #支援四則運算
6.8Q查詢
from django.db.models import Q
    # | -- or
    # & -- and
    # ~ -- not
ret = models.Book.objects.filter(Q(comment__gt=30)|Q(dianzan__gt=50))
ret = models.Book.objects.filter(Q(comment__gt=30)&Q(dianzan__gt=50))
# 等同於# 
ret = models.Book.objects.filter(comment__gt=30,dianzan__gt=50)
ret = models.Book.objects.filter(Q(comment__gt=30) | Q(dianzan__gt=50),publishDate__year='2018')
    # 注意沒有Q包裹的條件,寫在Q包裹的條件後面.
    
# 多層巢狀
ret = models.Book.objects.filter(Q(Q(comment__gt=30) | Q(dianzan__gt=50))&Q(xx=11),publishDate__year='2018')

# 條件取反
# 取評論數小於等於30 的,或者點贊數大於50的
ret = models.Book.objects.filter(~Q(comment__gt=30)|Q(dianzan__gt=50))
print(ret)

4.鎖和事務

事務:必須所有的操作全部成功,最終提交資料,否則資料回滾,回到剛開始沒操作的那個狀態,給sql語句加事務有兩種方式:1.裝飾器:@transaction.atomic (所有被裝飾的函式中所有sql語句都被捆綁為事務) ,2.with語句

鎖:create、update、delete操作時,mysql自動加行級互斥鎖,select_for_update()

from django.db import transaction
#方式一:
@transaction.atomic  
def xx(request):
    # a = 10
    # models......
    # a = a - 1
#方式二:
with transaction.atomic():
    models.Book.objects.filter(price=100).select_for_update()  #select * from app01_book where price=100 for update
 #with語句體裡面的sql都捆綁為事務

七.ajax

ajax用法:非同步請求 區域性重新整理

寫法:html中用jquery–>$.ajax({}) views.py檔案中:return HttpResponse(‘ok’)

html檔案:

<body>
{# <form action=""> #}
    使用者名稱: <input type="text" id="uname">
    密碼: <input type="password" id="pwd">
    <input type="button" value="確定" id="btn">
    <span id="error_msg" style="color:red;font-size: 12px;"></span>
{#</form>#}
</body>

<script src="{% static 'js/jquery.js' %}"></script>
<script>
    $('#btn').click(function () {
        var uname = $('#uname').val();
        var pwd = $('#pwd').val();
        $.ajax({
            url:'/login/',
            type:'post',
            data:{username:uname,password:pwd},  // request .POST.get('username')
            success:function (res) {
                // res接收的是請求成功之後的響應結果,
                // 這對響應結果做一些事情
                console.log(res, typeof res);
                if (res === 'ok'){
                    location.href = '/xx/';
                }else {
                    $('#error_msg').text('使用者名稱或者密碼有誤');
                }
            },
            error:function (res) {
                //res 請求失敗之後獲取到的響應結果  5xx,4xx
                console.log('>>>>>>',res);
                if (res.status === 403){
                    $('#error_msg').text(res.responseText);
                }
                //else if (res.status === 500){
                //}
            }
        })
    })
</script>

views.py檔案:

    def login(request):
        if request.method == 'GET':
            print('看登入頁面')
            return render(request, 'login.html')
        uname = request.POST.get('username')
        if uname == 'chao':
        # <QueryDict: {'username': ['root'], 'password': ['123']}>
        # return render(request,'xx.html')
            return HttpResponse('ok')
        else:
            ret = HttpResponse('使用者名稱不對')  # 200
            # ret.status_code = 403
            return ret

八.中介軟體

1.中介軟體相關概念

中介軟體是幫助我們在檢視函式執行之前和執行之後都可以做一些額外的操作,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法.

1.1 Django請求生命週期

在這裡插入圖片描述
傳送門

1.2 中介軟體的五個方法

1 process_request(self,request)

多箇中介軟體時,按setting檔案倒敘執行,

from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1裡面的 process_request")
        #不必須寫return值
    def process_response(self, request, response):#request和response兩個引數必須有,名字隨便取
        print("MD1裡面的 process_response")
        #print(response.__dict__['_container'][0].decode('utf-8')) #檢視響應體裡面的內容的方法,或者直接使用response.content也可以看到響應體裡面的內容,由於response是個變數,直接點選看原始碼是看不到的,你列印type(response)發現是HttpResponse物件,檢視這個物件的原始碼就知道有什麼方法可以用了。
     return response  #必須有返回值,寫return response  ,這個response就像一個接力棒一樣
        #return HttpResponse('瞎搞') ,如果你寫了這個,那麼你檢視返回過來的內容就被它給替代了

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裡面的 process_request")
        pass

    def process_response(self, request, response): #request和response兩個引數必須要有,名字隨便取
        print("MD2裡面的 process_response") 
        return response  #必須返回response,不然你上層的中介軟體就沒有拿到httpresponse物件,就會報錯

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

3 process_template_response(self,request,response)

4 process_exception(self, request, exception)

5 process_response(self, request, response)

必須有return值,且在檢視函式之後執行且多個是倒敘執行,

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class Md1(MiddlewareMixin):
    def process_request(self,request):
        print("Md1請求")
     #process_request方法裡面不寫返回值,預設也是返回None,如果你自己寫了return None,也是一樣的效果,不會中斷你的請求,但是如果你return 的一個httpresponse物件,那麼就會在這個方法中斷你的請求,直接返回給使用者,這就成了非正常的流程了
        #並且,如果你在這裡return了httpresponse物件,那麼會從你這個中介軟體類中的process_response方法開始執行返回操作,所以這個類裡面只要有process_response方法,肯定會執行
    def process_response(self,request,response):
        print("Md1返回")
        return response

class Md2(MiddlewareMixin):
    def process_request(self,request):
        print("Md2請求")
        #return HttpResponse("Md2中斷")
    def process_response(self,request,response):
        print("Md2返回")
        return response
中介軟體可以定義五個方法,分別是:(主要的是process_request和process_response)

process_request(self,request)
	執行順序:按setting檔案配置的順序從上到下依次執行.
	如果return了一個httpresponse物件則會在這個方法中斷你的請求,直接返回給使用者,不會走檢視函式
	
process_view(self, request, view_func, view_args, view_kwargs)
	它應該返回None或一個HttpResponse物件。 
	如果返回None,Django將繼續處理這個請求,執行任何其他中介軟體的process_view方法,然後在執行相應的檢視。 
	如果它返回一個HttpResponse物件,Django不會呼叫對應的檢視函式。它將執行中介軟體的process_response方法並將應用到該HttpResponse並返回結果。

process_template_response(self,request,response)
	process_template_response是在檢視函式執行完成後立即執行,
	但是它有一個前提條件,那就是檢視函式返回的物件有一個render()方法

process_exception(self, request, exception)
	處理檢視函式的異常順序是倒序
	這個方法只有在檢視函式中出現異常了才執行,它返回的值可以是一個None也可以是一個HttpResponse物件
	如果是HttpResponse物件,Django將呼叫模板和中介軟體中的process_response方法,並返回給瀏覽器,否則將預設處理異常
	如果返回一個None,則交給下一個中介軟體的process_exception方法來處理異常。它的執行順序也是按照中介軟體註冊順序的倒序執行。
	
process_response(self, request, response)
	執行順序:在檢視函式之後執行,按setting檔案配置的倒敘從下到上依次執行.

2.建立中介軟體流程

1 在應用下建立一個資料夾

2 在資料夾中建立一個py檔案

3 在py檔案中寫上如下內容

4 在settings.py檔案中做如下配置

py檔案內容:

from django.utils.deprecation import MiddlewareMixin

class Auth(MiddlewareMixin):  #類名隨意,繼承MiddlewareMixin
    # 如果想對請求做一些統一處理,那麼就定義process_request方法
    def process_request(self,request):
        print('請求來啦!!!快看!!!!')
        
        
    def process_response(self,request,response):
        # response 檢視響應回來的響應物件
        # response.status_code = 201
        print('請求走啦!!!')
        return response

settings檔案配置:

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',
    
    'app01.mymiddlewares.xx.Auth',  #將中介軟體類的路徑寫到這裡
]

九.cookie和session

1.什麼是cookie

http協議(基於tcp)的特點:1 無連線(短連線);2 無狀態.

1 無連線(短連線)
請求:connection : close,keepalive
2 無狀態
http協議不會記錄客戶端和服務端的任何資訊,導致服務端和客戶端不能維持會話

正是由於http無狀態的特性,所以出現了cookie(一種瀏覽器技術).請求頭健值對.

瀏覽器訪問服務端,帶著一個空的cookie,然後由伺服器產生內容,瀏覽器收到相應後儲存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上Cookie,這樣伺服器就能通過Cookie的內容來判斷這個是“誰”了。

在這裡插入圖片描述

2.cookie操作

Ctrl + Shift + del三個鍵來清除頁面快取和cookie

2.1例項(登入)

from django.shortcuts import render,redirect
# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    uname = request.POST.get('uname')
    if uname == 'root':
        ret = redirect('/home/')
        ret.set_cookie('is_login',True)
        return ret
    return redirect('/login/')

def home(request):
    if request.COOKIES.get('is_login') == 'True':
        return render(request,'home.html')
    return redirect('/login/')

裝飾器版:

from django.shortcuts import render,redirect
def outer(f):
    def inner(request,*args,**kwargs):
        if request.COOKIES.get('is_login') != 'True':
            return redirect('/login/')
        return f(request,*args,**kwargs)
    return inner

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    uname = request.POST.get('uname')
    if uname == 'root':
        ret = redirect('/home/')
        ret.set_cookie('is_login',True)
        return ret
    return redirect('/login/')

@outer
def home(request):
    return render(request,'home.html')

中介軟體版:

1 在app01資料夾下建mymildewares/auth.py

2 在setting檔案下配置路徑

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

class LoginAuth(MiddlewareMixin):
    #設定白名單
    white_list = ['/login/',]
    def process_request(self,request):
        if request.path not in self.white_list:
            if request.COOKIES.get('is_login') != 'True':
                return redirect('/login/')

setting檔案

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',
    'app01.mymildlewares.auth.LoginAuth'#配置路徑
]

view檔案:

from django.shortcuts import render,redirect

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    uname = request.POST.get('uname')
    if uname == 'root':
        ret = redirect('/home/')
        ret.set_cookie('is_login',True)
        return ret
    return redirect('/login/')

def home(request):
    return render(request,'home.html')

2.2cookie操作方法

獲取cookie
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) #帶簽名的值

設定cookie
rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)

修改cookie
ret.set_cookie('username', 'xxx')  #相同的鍵設定不同的值就是修改

刪除cookie
ret.delete_cookie('username')

引數說明:

引數:

      key, 鍵

      value='', 值

      max_age=None, 超時時間,單位為秒  5表示5秒後失效

      expires=None, 超時時間(IE requires expires, so set it if hasn't been already.) 值為時間日期型別資料

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

      domain=None, Cookie生效的域名

      secure=True, https傳輸

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

3.什麼是session

產生原因:

1 cookie是明文儲存的

2 大小限制:

Cookie大小上限為4KB;
一個伺服器最多在客戶端瀏覽器上儲存20個Cookie;
一個瀏覽器最多儲存300個Cookie,因為一個瀏覽器可以訪問多個伺服器。

特點:

cookie中放的資料是密文的

資料存在服務端,沒有大小上限

一個瀏覽器對應一個服務端,就是一個session

原理:

在這裡插入圖片描述

4.操作session

4.1例項(登入)

views檔案:

from django.shortcuts import render,redirect
def login(request):
    if request.method == "GET":
        return render(request,'login.html')
    if request.POST.get('uname') == 'root':
        request.session['is_login'] = True  #設定
        return redirect('/home/')
    return redirect('/login/')

def home(request):
    return render(request,'home.html')

中介軟體:

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

class LoginAuth(MiddlewareMixin):
    white_list = ['/login/',]
    def process_request(self,request):
        if request.path not in self.white_list:
            if request.session.get('is_login') != True: #獲取
                return redirect('/login/')

資料庫配置:

python manage.py makemigrations
python manage.py migrate

4.2session操作方法

#設定session
request.session['k1'] = 123
# request.session
#1 生成一個隨機字串
#2 將隨機字串放到cookie中,名稱為sessionid
#3 將設定的session資料,序列化+加密,儲存到了django-session表中
request.session.setdefault('k1',123)

#獲取session
request.session['k1']
# request.session['is_login']
# 1 取出請求中cookie鍵為sessionid的值
# 2 通過這個值到django-session表中獲取資料
# 3 將資料解密並且反序列化得到原來的資料
request.session.get('k1',None)

#刪除session
del request.session['k1']

#清空session
request.session.flush()

# 登出
def logout(requset):
    requset.session.flush()
    # 1 刪除cookie中的資料
    # 2 刪除資料庫中django-session表中的記錄
    return redirect('/login/')

補充:django外部指令碼呼叫django環境

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

from app01 import  models
models.Book.objects.create(
    title='xxx',
    price=200,
)

十.CSRF

csrf: 跨站請求偽造

在這裡插入圖片描述
解決csrf攻擊的最直接的辦法就是生成一個隨機的csrftoken值,儲存在使用者的頁面上,每次請求都帶著這個值過來完成校驗,django會驗證表單中的token和cookie中token是否能解出同樣的secret,secret一樣則本次請求合法 .

form表單的csrf認證:

{% csrf_token %}

ajax的csrf認證(需要從cookie中拿取token新增到請求頭中):

//方式一:
$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  data: {
    "username": "chao",
    "password": 123456,
    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
  },
  success: function (data) {
    console.log(data);
  }
})
//方式二:
$.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});

十一.檔案上傳

form表單上傳檔案

<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    使用者名稱:<input type="text" name="username" >
    頭像:<input type="file" name="avatar" multiple>
    <input type="submit">
</form>

ajax上傳檔案

使用者名稱:<input type="text" name="username" id="username">
頭像:<input type="file" name="avatar" id="avatar">
<button id="ajax_btn">上傳</button>


    $('#ajax_btn').click(function () {

        var uname = $('#username').val();
        var file_obj = $('#avatar')[0].files[0];

        var formdata = new FormData();
        formdata.append('username',uname);
        formdata.append('csrfmiddlewaretoken','{{ csrf_token }}');
        formdata.append('avatar',file_obj);

        $.ajax({
            url:'/login/',
            type:'post',
            data:formdata,
            processData: false ,    // 不處理資料
            contentType: false,    // 不設定內容型別
            success:function (res) {
                console.log(res);
            }
        })
    })

檢視程式碼:

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        # print(request.POST)
        # print(request.FILES)
        file_obj = request.FILES.get('avatar')
        print(file_obj)
        name = file_obj.name
        print(name)
        # with open(fr'C:\Users\oldboy\Desktop\Pointofix\{name}', 'wb') as f:
        print(type(file_obj))
        with open(name, 'wb') as f:
			# 方式1
            # for i in file_obj:  # \r\n
			# 方式2
            # for i in file_obj.chunks():  # \r\n
            for i in file_obj.chunks():  # \r\n  讀取65536B
                f.write(i)
        return HttpResponse('ok')

相關文章