利用HTTP協(xié)議向服務(wù)器傳參的幾種途徑勤讽、響應(yīng)厢漩、Cookie膜眠、Session、類視圖溜嗜、中間件
注意:
? 1>Django中的QueryDict對象
? ? ? django 中有一個 QueryDict 對象宵膨,這個對象一般用來存儲瀏覽器傳遞過來的參數(shù),我們可以默認(rèn)把他看成是一個字典
? ? ? 但是它和一般的字典的不同之處在于:
? ? ? ? ? ■ QueryDict 這個字典可以用來處理一個鍵帶有多個值得情況炸宵,即: 一鍵 ===> 多值
? ? ? QueryDict 這個字典支持 get( ) 方法的使用:
? ? ? ? ? ■ 這個位置需要區(qū)分是一鍵一值還是一鍵多值的不同情況:
? ? ? ? ? ? ? 如果是一鍵一值:
QueryDict.get( key ) 獲取的就是當(dāng)前對應(yīng)的值(value)
? ? ? ? ? ? ? 如果是一鍵多值:
QueryDict.get( key ) 獲取的就是最后一個值
? ? ? ? ? ? ? QueryDict 還有一個所有值得方法:
QueryDict.getlist( key ) 獲取這個鍵對應(yīng)的所有值.
? 例如:
? ? ? get( ):
根據(jù)鍵獲取值
如果一個鍵同時擁有多個值將獲取最后一個值
如果鍵不存在則返回None值辟躏,可以設(shè)置默認(rèn)值進行后續(xù)處理
# 默認(rèn)值一般是default,可以省略
QueryDict.get('鍵',默認(rèn)值)
# 或可寫為:
QueryDict['鍵']
? ? ? getlist( ):
根據(jù)鍵獲取值,值以列表返回土全,可以獲取指定鍵的所有值
如果鍵不存在則返回空列表[]捎琐,可以設(shè)置默認(rèn)值進行后續(xù)處理
dict.getlist('鍵',默認(rèn)值)
? 2>Django中CSRF保護的開關(guān)
? ? ? Django默認(rèn)開啟了CSRF防護,會對POST裹匙、PUT瑞凑、PATCH、DELETE請求方式進行CSRF防護驗證
? ? ? 在使用postman測試時可以關(guān)閉CSRF防護機制
? ? ? 關(guān)閉CSRF防護方法為在settings.py文件中注釋掉CSRF中間件概页,如:
? 3>Django中的request.GET和request.POST
? ? ? 查詢字符串不區(qū)分請求方式籽御,即假使客戶端進行POST方式的請求,依然可以通過request.GET獲取請求中的查詢字符串?dāng)?shù)據(jù)惰匙。
? ? ? request.GET 指的不是發(fā)送ajax用的get方法, 而是指從url的查詢字符串中獲取參數(shù)
? ? ? request.POST 指的也不是ajax用的post方法,而是我們從請求體中獲取的參數(shù)
? ? ? 通過上面大家就能明白: GET 和 POST 這里指的都是獲取參數(shù)的位置, 而不是我們以前說的get請求和post請求
? ? ? 如果是從url中(通過查詢字符串)傳遞參數(shù)技掏,使用GET(代表的是傳遞參數(shù)的位置),和請求方法無關(guān)
? ? ? 如果是從請求體中傳遞參數(shù)使用POST
? 4>postman請求體傳遞參數(shù)的4個選項
? ? ? form-data多元素表單
? ? ? x-www-form-urlencoded 正常表單
? ? ? raw (非表單)可以選擇傳輸數(shù)據(jù)的數(shù)據(jù)類型
? ? ? binary二進制
1.利用HTTP協(xié)議向服務(wù)器傳參的幾種途徑
<1>flask框架
? 1>通過URL的特定部分傳參
? ? ? 提取URL的特定部分项鬼,如/weather/2018哑梳,可以在服務(wù)器端的路由中用正則轉(zhuǎn)換器截取
# flask后端接收數(shù)據(jù):
? # 使用正則轉(zhuǎn)換器
? # /user/weather/2018
? @app.route('/user/weather/<re: userid>')
? def index(userid):
print(userid) # 2018
? ? ? return '%s' % userid
? 2>通過查詢字符串(query string)傳遞參數(shù),url中?后形如key1=value1&key2=value2
? ? ? 獲取參數(shù)的方式
request.args.get('key1')
? 3>請求體(body)中發(fā)送的數(shù)據(jù)绘盟,比如表單數(shù)據(jù)鸠真、json悯仙、xml
? ? ? 獲取參數(shù)的方式
request.json.get('key')
? 4>通過http請求報文的請求頭(header)傳遞參數(shù)。
? ? ? 獲取參數(shù)的方式
request.headers.get('content-type')
<2>Django
? 1>通過URL的特定部分傳遞參數(shù)
? ? ? 獲取參數(shù)的方式:視圖函數(shù)傳參
? ? ? ? ? ■ 在定義路由URL時弧哎,可以使用正則表達式提取參數(shù)的方法從URL中獲取請求參數(shù)雁比,Django會將提取的參數(shù)直接傳遞到視圖的傳入?yún)?shù)中。
? ? ? ? ? ■ 未命名參數(shù)按定義順序傳遞(相當(dāng)于位置參數(shù))撤嫩, 如
? url(r'^weather/([a-z]+)/(\d{4})/$', views.weather),
? def weather(request, city, year):
? ? ? print('city=%s' % city) # beijing
? ? ? print('year=%s' % year) # 2008
? ? ? return HttpResponse('OK')
? 命名參數(shù)按名字傳遞(相當(dāng)于關(guān)鍵字參數(shù))偎捎,如
? url(r'^weather/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.weather),
? def weather(request, year, city):
? ? ? print('city=%s' % city)
? ? ? print('year=%s' % year)
? ? ? return HttpResponse('OK')
? 2>通過請求路徑url中的查詢字符串傳遞參數(shù)(形如?k1=v1&k2=v2)
? ? ? 獲取參數(shù)的方式:request.GET.get('key')
? ? ? ? ? ■ 可以通過request.GET屬性獲取,這個方法返回QueryDict對象序攘。
? ? ? ? ? ■ QueryDict對象類似于一個字典, 所以我們可以通過get() 來獲取 key 值所對應(yīng)的 value 值
a = request.GET.get('a')
? ? b = request.GET.get('b')
? ? alist = request.GET.getlist('a')
? 3>通過請求體傳遞參數(shù)
? ? ? 請求體數(shù)據(jù)格式不固定茴她,可以是表單類型字符串,可以是JSON字符串程奠,可以是XML字符串丈牢,應(yīng)區(qū)別對待。
? ? ? 可以發(fā)送請求體數(shù)據(jù)的請求方式有 POST瞄沙、PUT己沛、PATCH、DELETE距境。
? ? ? 請求體—表單類型 (Form Data)
? ? ? ? ? ■ 獲取參數(shù)的方式:request.POST.get('key')
? ? ? ? ? ■ 前端發(fā)送的表單類型的請求體數(shù)據(jù)申尼,可以通過request.POST屬性獲取,返回QueryDict對象垫桂。
? ? ? ? ? ■ QueryDict對象類似于一個字典, 所以我們可以通過get() 來獲取 key 值所對應(yīng)的 value 值
a = request.POST.get('a')
b = request.POST.get('b')
alist = request.POST.getlist('a')
? 注意:
? ? ? 如果是表單數(shù)據(jù), 發(fā)送的請求不但要求body中是表單的鍵值對
? ? ? 也要求請求頭中content-type的類型是application/x-www-form-urlencoded
? ? ? 如果請求中的不是這樣寫的類型, 上面的request.POST.get( )方法也不可以使用.
? ? ? 如圖所示: (這兩部分都有才可以使用上面的request.POST.get( )方法進行請求)
? ? ? 請求體——非表單類型 (Non-Form Data)
? ? ? ? ? ■ 非表單類型的請求體數(shù)據(jù)师幕,Django無法自動解析
? ? ? ? ? ■ 可以通過request.body屬性獲取最原始的請求體數(shù)據(jù),自己按照請求體格式(JSON诬滩、XML等)進行解析霹粥。
? ? ? ? ? ■ 其中: request.body返回bytes類型。
? ? json_bytes = request.body
? ? json_str = json_bytes.decode()
? ? #python3.6及以上版本中, json.loads()方法可以接收str和bytes類型
? ? #但是python3.5以及以下版本中, json.loads()方法只能接收str,
? ? #所以我們的版本如果是3.5以及以下 需要將bytes類型解碼為str類型
? ? req_data = json.loads(json_str)
? ? print(req_data['a'])
? ? print(req_data['b'])
? 4>通過請求頭傳遞參數(shù)
? ? ? 我們可以通過request.META屬性獲取請求頭headers中的數(shù)據(jù)
? ? ? request.META為字典類型疼鸟。
? ? ? 注意請求頭的用法區(qū)別后控,key的表現(xiàn)形式不同
? ? ? ? ? ■ 我們通過request.META獲取的時候,需要使用如下所示的用法:
? ? ? ? ? ■ CONTENT_LENGTH – The length of the request body (as a string).
? ? ? ? ? ■ CONTENT_TYPE – The MIME type of the request body.
? ? ? ? ? ■ HTTP_ACCEPT – Acceptable content types for the response.
? ? ? ? ? ■ HTTP_ACCEPT_ENCODING – Acceptable encodings for the response.
? ? ? ? ? ■ HTTP_ACCEPT_LANGUAGE – Acceptable languages for the response.
? ? ? ? ? ■ HTTP_HOST – The HTTP Host header sent by the client.
? ? ? ? ? ■ HTTP_REFERER – The referring page, if any.
? ? ? ? ? ■ HTTP_USER_AGENT – The client’s user-agent string.
? ? ? ? ? ■ QUERY_STRING – The query string, as a single (unparsed) string.
? ? ? ? ? ■ REMOTE_ADDR – The IP address of the client.
? ? ? ? ? ■ REMOTE_HOST – The hostname of the client.
? ? ? ? ? ■ REMOTE_USER – The user authenticated by the Web server, if any.
? ? ? ? ? ■ REQUEST_METHOD – A string such as "GET" or "POST".
? ? ? ? ? ■ SERVER_NAME – The hostname of the server.
? ? ? ? ? ■ SERVER_PORT – The port of the server (as a string).
request.META['CONTENT_TYPE']
<3>其他常用的HttpRequest對象屬性
? 1>method:一個字符串,表示請求使用的HTTP方法空镜,常用值包括:'GET'忆蚀、'POST'。
? 2>user:請求的用戶對象姑裂。
? 3>path:一個字符串,表示請求的頁面的完整路徑男旗,不包含域名和參數(shù)部分舶斧。
? 4>encoding:一個字符串,表示提交的數(shù)據(jù)的編碼方式察皇。
? ? ? 如果為None則表示使用瀏覽器的默認(rèn)設(shè)置茴厉,一般為utf-8泽台。
? ? ? 這個屬性是可寫的,可以通過修改它來修改訪問表單數(shù)據(jù)使用的編碼矾缓,接下來對屬性的任何訪問將使用新的encoding值怀酷。
? 5>FILES:一個類似于字典的對象,包含所有的上傳文件嗜闻。
2.響應(yīng)
? 視圖在接收請求并處理后蜕依,必須返回HttpResponse對象或子對象。
? HttpRequest對象由Django創(chuàng)建琉雳,HttpResponse對象由開發(fā)人員創(chuàng)建样眠。
<1>HttpResponse類
? 1>HttpResponse的使用方式
? ? ? 可以從 django.http里面導(dǎo)入HttpResponse
? ? ? from django.http import HttpResponse
HttpResponse(
? ? content=響應(yīng)體,
? ? content_type=響應(yīng)體數(shù)據(jù)類型,
? ? status=狀態(tài)碼
)
? ? ? 通過上式我們知道HttpResponse里面有對應(yīng)的一些參數(shù)可以修改:
? ? ? ? ? ■ content:表示返回的內(nèi)容。
? ? ? ? ? ■ status_code:返回的HTTP響應(yīng)狀態(tài)碼翠肘。
? ? ? ? ? ■ content_type:指定返回數(shù)據(jù)的的MIME類型檐束。
? ? ? 例如:
# 定義一個新的視圖函數(shù)
def demo_response(request):
? ? # 定義一個json字符串
? ? s = '{"name": "python"}'
? ? # 返回一個HttpResponse響應(yīng)對象
? ? return HttpResponse(s, content-type="application/json", status=400)
? 2>HttpResponse特別的使用方式:
? ? ? 我們?nèi)绻枰陧憫?yīng)頭添加自定義的鍵值對內(nèi)容,可以把HttpResponse對象當(dāng)做字典進行響應(yīng)頭鍵值對的設(shè)置:
response = HttpResponse()
# 自定義響應(yīng)頭Itcast, 值為Python
response['Itcast'] = 'Python'
示例:
from django.http import HttpResponse
def demo_view(request):
? ? return HttpResponse('itcast python', status=400)
? ? 或者
? ? response = HttpResponse('itcast python')
? ? response.status_code = 400
? ? response['Itcast'] = 'Python'
? ? return response
<2>HttpResponse子類
? Django提供了一系列HttpResponse的子類束倍,可以快速設(shè)置狀態(tài)碼
? 這個狀態(tài)碼可以從 Django.http 里面導(dǎo)入,例如:
? from django.http import HttpResponseNotFound
? ? ? HttpResponseRedirect 301
? ? ? HttpResponsePermanentRedirect 302
? ? ? HttpResponseNotModified 304
? ? ? HttpResponseBadRequest 400
? ? ? HttpResponseNotFound 404
? ? ? HttpResponseForbidden 403
? ? ? HttpResponseNotAllowed 405
? ? ? HttpResponseGone 410
? ? ? HttpResponseServerError 500
? 使用的演示:
<3>JsonResponse
? 如果我們要返回json字符串, 那么我們可以使用 JsonResponse 來幫助我們快速的構(gòu)建json字符串,進行返回.
? JsonResponse 能夠幫助我們自動把字典轉(zhuǎn)成json字符串類型, 并且還不用自己設(shè)置響應(yīng)頭中contentType字段
? 1>JsonResponse的作用
? ? ? 幫助我們將數(shù)據(jù)轉(zhuǎn)換為json字符串
? ? ? 設(shè)置響應(yīng)頭Content-Type為 application/json
? 2>JsonResponse的使用
# 導(dǎo)入JsonResponse
from django.http import JsonResponse
def demo_view(request):
? ? # 直接返回JsonResponse這個對象,并且里面可以直接傳入?yún)?shù)
? ? return JsonResponse({'city': 'beijing', 'subject': 'python'})
<4>redirect重定向
? 建議redirect(重定向)和我們前面學(xué)習(xí)的reverse(反解析)搭配使用.
? ? ? return redirect(reverse('reqrespspace:getresponse_name'))
? ? ? ? ? ■ reverse('spacename:name')
? 盡量不要把路由寫死. 有利于我們更改開發(fā)代碼.
from django.shortcuts import redirect
def demo_view(request):
? ? return redirect('/index.html')
3.Cookie
? Cookie是存儲在瀏覽器中的一段純文本信息被丧,建議不要存儲敏感信息如密碼,因為電腦上的瀏覽器可能被其它人使用绪妹。
? Cookie甥桂,有時也用其復(fù)數(shù)形式Cookies,指某些網(wǎng)站為了辨別用戶身份喂急、進行session跟蹤而儲存在用戶本地終端上的數(shù)據(jù)(通常經(jīng)過加密)格嘁。
? Cookie最早是網(wǎng)景公司的前雇員Lou Montulli在1993年3月的發(fā)明。
? Cookie是由服務(wù)器端生成廊移,發(fā)送給User-Agent(一般是瀏覽器)糕簿,瀏覽器會將Cookie的key/value保存到某個目錄下的文本文件內(nèi),下次請求同一網(wǎng)站時就發(fā)送該Cookie給服務(wù)器(前提是瀏覽器設(shè)置為啟用cookie)狡孔。
? Cookie名稱和值可以由服務(wù)器端開發(fā)自己定義懂诗,這樣服務(wù)器可以知道該用戶是否是合法用戶以及是否需要重新登錄等。服務(wù)器可以利用Cookies包含信息的任意性來篩選并經(jīng)常性維護這些信息苗膝,以判斷在HTTP傳輸中的狀態(tài)殃恒。
? Cookies最典型記住用戶名。
<1>Cookie的特點
? Cookie以鍵值對的格式進行信息的存儲辱揭。
? Cookie基于域名安全离唐,不同域名的Cookie是不能互相訪問的
? ? ? 如訪問taobao.com時向瀏覽器中寫了Cookie信息,使用同一瀏覽器訪問baidu.com時问窃,無法訪問到taobao.com寫的Cookie信息亥鬓。
? 當(dāng)瀏覽器請求某網(wǎng)站時,會將瀏覽器存儲的跟網(wǎng)站相關(guān)的所有Cookie信息提交給網(wǎng)站服務(wù)器域庇。
<2>設(shè)置Cookie
? 可以通過HttpResponse對象中的set_cookie方法來設(shè)置cookie嵌戈。
HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)
? ? ? max_age 單位為秒覆积,默認(rèn)為None。
? ? ? 如果是臨時cookie熟呛,可將max_age設(shè)置為None宽档。
? 示例:
def demo_view(request):
? ? response = HttpResponse('ok')
? ? response.set_cookie('0001', 'python1')? # 臨時cookie
? ? response.set_cookie('0002', value='python2', max_age=3600)? # 有效期一小時
? ? return response
<3>獲取Cookie
? 可以通過HttpRequest對象的COOKIES屬性來讀取本次請求攜帶的cookie值。request.COOKIES為字典類型庵朝。
? request.COOKIES.get('key')
def demo_view(request):
? ? cookie1 = request.COOKIES.get('0001')
? ? print(cookie1)
? ? return HttpResponse('OK')
4.Session
<1>啟用Session
? 1>Session簡介:
? ? ? 在計算機中吗冤,尤其是在網(wǎng)絡(luò)應(yīng)用中,稱為“會話控制”偿短。Session 對象存儲特定用戶會話所需的屬性及配置信息欣孤。這樣,當(dāng)用戶在應(yīng)用程序的 Web 頁之間跳轉(zhuǎn)時昔逗,存儲在 Session 對象中的變量將不會丟失降传,而是在整個用戶會話中一直存在下去。當(dāng)用戶請求來自應(yīng)用程序的 Web 頁時勾怒,如果該用戶還沒有會話婆排,則 Web 服務(wù)器將自動創(chuàng)建一個 Session 對象。當(dāng)會話過期或被放棄后笔链,服務(wù)器將終止該會話段只。Session 對象最常見的一個用法就是存儲用戶的首選項。例如鉴扫,如果用戶指明不喜歡查看圖形赞枕,就可以將該信息存儲在 Session 對象中。注意 會話狀態(tài)僅在支持 cookie 的瀏覽器中保留坪创。
? 2>Session的理解:
? ? ? 廣義來講: session是一種會話機制, 用于記錄多次http請求之間的關(guān)系,關(guān)系就是狀態(tài)數(shù)據(jù),比如登錄狀態(tài).
? ? ? 狹義來講: session是一種會話數(shù)據(jù), 記錄的狀態(tài)數(shù)據(jù), 比如登錄之后記錄的user_id等
? 3>Django項目默認(rèn)啟用Session
? ? ? 可以在settings.py文件的中間件選項中查看炕婶,如圖所示
? ? ? 如需禁用session,將上圖中的session中間件注釋掉即可莱预。
<2>存儲方式
? 在settings.py文件中柠掂,可以設(shè)置session數(shù)據(jù)的存儲方式.
? 另外session可以保存在數(shù)據(jù)庫、本地緩存( 程序的運行內(nèi)存中, 全局變量)依沮、文件涯贞、redis等 。
? 1>數(shù)據(jù)庫
? ? ? 存儲在數(shù)據(jù)庫中危喉,如下設(shè)置可以寫到settings中宋渔,也可以不寫,這是默認(rèn)存儲方式辜限。
# 如果是存放數(shù)據(jù)庫, 一般以db結(jié)尾
SESSION_ENGINE='django.contrib.sessions.backends.db'
? ? ? 如果存儲在數(shù)據(jù)庫中傻谁,需要在項INSTALLED_APPS中安裝Session應(yīng)用。
? ? ? 數(shù)據(jù)庫中的表如圖所示
? ? ? 表結(jié)構(gòu)如下
? ? ? 由表結(jié)構(gòu)可知列粪,操作Session包括三個數(shù)據(jù):鍵审磁,值,過期時間岂座。
? 2>本地緩存
? ? ? 存儲在本機內(nèi)存中态蒂,如果丟失則不能找回,比數(shù)據(jù)庫的方式讀寫更快费什。
# 如果是存放在本地緩存, 一般以cache結(jié)尾
SESSION_ENGINE='django.contrib.sessions.backends.cache'
? ? ? 其中,本地緩存會出現(xiàn)問題: 因為是存放在本地的內(nèi)存中,所以會出現(xiàn)在脫機情況下出現(xiàn)的跨機訪問問題:
? ? ? ? ? ■ 我們這里可以看到: 有兩臺服務(wù)器存儲的有session數(shù)據(jù), 前面由nginx負(fù)責(zé)管理訪問機制,有可能現(xiàn)在的訪問方式是輪詢形式, 那么意味著第一次用戶進入的是上面的服務(wù)器,進行了登錄操作,我們把他的登錄狀態(tài)保存到了服務(wù)器1里面, 隨后用戶有進行了其他操作, 然后有登陸進入這個服務(wù)器,這次輪詢到了服務(wù)器2里面,但是這里面沒有保存登錄狀態(tài),這樣就會造成用戶第二次登錄.所以會造成用戶跨機的問題.
? ? ? ? ? ■ 但是如果我們使用redis就不會出現(xiàn)這樣的情況,因為無論是哪一個服務(wù)器,最終存儲的數(shù)據(jù)都保存到了redis中 :
? 3>混合存儲
? ? ? 優(yōu)先從本機內(nèi)存中存取钾恢,如果沒有則從數(shù)據(jù)庫中存取。
# 如果是存放數(shù)據(jù)庫,一般以cached_db結(jié)尾
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
? 4>Redis
? ? ? django-redis 中文文檔: http://django-redis-chs.readthedocs.io/zh_CN/latest/#cache-backend
? ? ? 在redis中保存session鸳址,需要引入第三方擴展瘩蚪,我們可以使用django-redis來解決。
? ? ? 安裝擴展
pip install django-redis
? ? ? 在settings.py文件中做如下設(shè)置
CACHES = {
? ? "default": {
? ? ? ? "BACKEND": "django_redis.cache.RedisCache",
? ? ? ? # 定義django中redis的位置
? ? ? ? "LOCATION": "redis://127.0.0.1:6379/0",
? ? ? ? "OPTIONS": {
? ? ? ? ? ? # django使用redis的默認(rèn)客戶端來進行操作.
? ? ? ? ? ? "CLIENT_CLASS": "django_redis.client.DefaultClient",
? ? ? ? }
? ? }
}
# 我們定義一個cache(本地緩存來存儲信息,cache指定的是redis)
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# 本地的session使用的本地緩存名稱是'default', 這個名稱就是上面我們配置的caches的名
# 稱"default"
SESSION_CACHE_ALIAS = "default"
? ? ? 添加代碼,查看是否能夠存儲到redis中去:
urls.py:
# url的配置:
from django.conf.urls import url
from . import views
urlpatterns = [
? ? # 保存session數(shù)據(jù)
? ? url(r'^set_session', views.set_session),
? ? # 獲取session數(shù)據(jù)
? ? url(r'^get_session', views.get_session),
]
Views.py:
# 定義設(shè)置session的視圖函數(shù)
def set_session(request):
? ? request.session['one'] = '1'
? ? request.session['two'] = '2'
? ? request.session['three'] = '3'
? ? return HttpResponse('保存session數(shù)據(jù)成功')
# 定義獲取session的視圖函數(shù)
def get_session(request):
? ? ? one = request.session.get('one')
? ? ? two = request.session.get('two')
? ? ? three = request.session.get('three')
? ? ? text = 'one=%s, two=%s, three=%s' % (one,two,three)
? ? ? return HttpResponse(text)
? ? ? 打開redis來查看存儲的信息:
打開redis:
redis-server
查看信息: (在新窗口中)
redis-cli
select 1
keys *
<3>Session操作
? 通過HttpRequest對象的session屬性進行會話的讀寫操作稿黍。
? 1>設(shè)置session
? ? ? 以鍵值對的格式寫session
? ? ? request.session['鍵']=值
例如: request.session['one'] = '1'
? 2>獲取session
? ? ? 根據(jù)鍵讀取值
? ? ? request.session.get('鍵')
例如: one = request.session.get('one')
? 3>清除所有session疹瘦,在存儲中刪除值部分
request.session.clear()
? 4>清除session數(shù)據(jù),在存儲中刪除session的整條數(shù)據(jù)巡球。
request.session.flush()
? 5>刪除session中的指定鍵及值言沐,在存儲中只刪除某個鍵及對應(yīng)的值。
del request.session['鍵']
? 6>設(shè)置session的有效期
request.session.set_expiry(value)
? ? ? 如果value是一個整數(shù)酣栈,session將在value秒沒有活動后過期险胰。
? ? ? 如果value為0,那么用戶session的Cookie將在用戶的瀏覽器關(guān)閉時過期矿筝。
? ? ? 如果value為None起便,那么session有效期將采用系統(tǒng)默認(rèn)值,默認(rèn)為兩周窖维,可以通過在settings.py中設(shè)置SESSION_COOKIE_AGE來設(shè)置全局默認(rèn)值榆综。其中 SESSION_COOKIE_AGE的單位是以秒為單位的.
5.類視圖
<1>類視圖
? 1>視圖函數(shù)
? ? ? 以函數(shù)的方式定義的視圖稱為函數(shù)視圖,即我們常說的視圖函數(shù)
? 2>視圖函數(shù)的缺點
? ? ? 視圖函數(shù)遭遇不同方式分別請求( 例如 get 和 post )陈辱,并且需要做不同處理時换淆,我們?nèi)绻谝粋€函數(shù)中編寫不同的業(yè)務(wù)邏輯,代碼可讀性和復(fù)用性都不好
? 3>類視圖
? ? ? 使用類來定義的視圖绿贞,稱為類視圖
? ? ? 類視圖中的方法通常以請求方式命名
? ? ? 在Django中給我們提供了類視圖的概念废登,即可以使用類來定義一個視圖,解決了視圖函數(shù)的缺點
<2>類視圖使用
? 1>如果想要使用類視圖需要如下幾步:
? ? ? 定義類視圖, 且類視圖繼承自View
? ? ? ? ? ■ 使用: from django.views.generic import View
? ? ? ? ? ■ 或者是: from django.views.generic.base import View
? ? ? 定義路由利赋,路由的第二個參數(shù)需要是一個函數(shù)水评,所以我們會調(diào)用系統(tǒng)的 as_view() 方法
urlpatterns = [
? ? # 類視圖:注冊
? ? # url(路徑, 執(zhí)行的函數(shù))
? ? # url的第二個參數(shù)需要是一個函數(shù)
? ? # 我們這里如果傳入: views.RegisterView 會發(fā)現(xiàn)這個是一個類, 不是一個函數(shù),
? ? # 所以我們需要調(diào)用系統(tǒng)給我們提供的 as_view() 方法
? ? url(r'^register/$', views.RegisterView.as_view()),
]
? ? ? 使用瀏覽器訪問我們定義的路由, 查看結(jié)果
? 2>使用類視圖可以將視圖對應(yīng)的不同請求方式以類中的不同方法來區(qū)別定義
如下所示:
# 導(dǎo)入類視圖的父類View
from django.views.generic import View
class RegisterView(View):
? ? """類視圖:處理注冊"""
? ? def get(self, request):
? ? ? ? """處理GET請求,返回注冊頁面"""
? ? ? ? return render(request, 'register.html')
? ? def post(self, request):
? ? ? ? """處理POST請求媚送,實現(xiàn)注冊邏輯"""
? ? ? ? return HttpResponse('這里實現(xiàn)注冊邏輯')
? 3>類視圖的好處:
? ? ? 代碼可讀性好
? ? ? 類視圖相對于函數(shù)視圖有更高的復(fù)用性
? ? ? 如果其他地方需要用到某個類視圖的某個特定邏輯中燥,直接繼承該類視圖即可
? 4>注意:
? ? ? 如果我們在類視圖函數(shù)中沒有定義方法, 但是我們請求了. 會報405找不到請求方法的錯誤.
? ? ? ? ? ■ 例如: 類視圖中沒有定義get方法, 但是我們使用get方法進行了請求, 那么會報405的錯誤: 找不到對應(yīng)的請求方法.
? ? ? 在類視圖中定義的get或者是post都是對象方法, 第一個參數(shù)都是self.
? ? ? 第二個參數(shù)一般情況下都是 request對象. 其他的參數(shù)依次往后面寫就可以.
? ? ? 我們在使用類視圖的時候, 需要在路由位置進行設(shè)置, 設(shè)置的第二個參數(shù)需要是一個函數(shù), 所以我們這里調(diào)用了類以后, 后面需要調(diào)用 as_view( ) 函數(shù).
<3>類視圖原理
? 為什么我們定義url的時候, 調(diào)用 as_view() 函數(shù),就可以達到結(jié)果, 如果不調(diào)用就會報錯
? as_view()的原理
? ? ? 由源碼可看出as_view()的作用是返回真正的視圖函數(shù)
? ? @classonlymethod
? ? def as_view(cls, **initkwargs):
? ? ? ? ...省略代碼...
? ? ? ? def view(request, *args, **kwargs):
? ? ? ? ? ? # 這里的cls是as_view這個函數(shù)接收的第一個參數(shù),也就是調(diào)用當(dāng)前函數(shù)的類
? ? ? # 得到調(diào)用的類了之后, 創(chuàng)建類的對象: self
? ? ? ? ? ? self = cls(**initkwargs)
? ? ? ? ? ? if hasattr(self, 'get') and not hasattr(self, 'head'):
? ? ? ? ? ? ? ? self.head = self.get
? ? ? ? ? ? # 給當(dāng)前這個類,添加對應(yīng)的屬性, 如下所示:
? ? ? ? ? ? self.request = request
? ? ? ? ? ? self.args = args
? ? ? ? ? ? self.kwargs = kwargs
? ? ? ? ? ? # 調(diào)用dispatch方法,按照不同請求方式調(diào)用不同請求方法
? ? ? ? ? ? return self.dispatch(request, *args, **kwargs)
? ? ? ? ...省略代碼...
? ? ? ? # 返回真正的函數(shù)視圖
? ? ? ? return view
? ? # dispatch本身是分發(fā)的意思,這里指的是函數(shù)的名字.
? ? def dispatch(self, request, *args, **kwargs):
? ? ? ? # self.http_method_names指的是我們的類視圖中方法的名字
? ? ? ? ? ? ? # 這里把所有方法的名字都存放在了http_methods_names中
? ? ? ? ? ? ? # 我們會把當(dāng)前請求的方式轉(zhuǎn)為小寫,然后判斷是否在列表中存在.
? ? ? ? if request.method.lower() in self.http_method_names:
? ? ? ? ? ? """
? ? 如果在里面, 則進入這里
? ? 這里的getattr作用是獲取當(dāng)前對象的屬性.
? ? 下面的參數(shù)為:
? ? self :? 類視圖對象
? ? request.method.lower() : 請求方法的小寫. 例如: 'get' 或 'post'
? ? http_method_not_allowed : 后續(xù)處理方式(不允許請求)
? ? 下面代碼整體的意思: 根據(jù)類視圖對象, 獲取當(dāng)前類視圖中對應(yīng)名稱的方法
? ? 如果獲取到, 則把方法返回給handle, 否則不允許訪問.
? ? """
? handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
? ? ? ? ? # 如果類視圖中如果沒有的話, 則進入這里, 表明不允許進行請求.
? ? ? ? ? ? # 我們會把不允許請求這個字段返回給handle.
? ? ? ? ? handler = self.http_method_not_allowed
? ? ? # 最終返回handle(handle里面要么包含可以訪問的方法, 要么就是不允許訪問的字段)
? ? ? return handler(request, *args, **kwargs)
<4>類視圖使用裝飾器
? 先定義一個為函數(shù)視圖準(zhǔn)備的裝飾器(在設(shè)計裝飾器時基本都以函數(shù)視圖作為考慮的被裝飾對象)塘偎,以及一個要被裝飾的類視圖
#? 定義一個裝飾器
def my_decorator(func):
? ? # wrapper函數(shù)必然會接收一個request對象,因為傳入進來的func這個函數(shù)肯定會帶這個參數(shù)
? ? def wrapper(request, *args, **kwargs):
? ? ? ? print('自定義裝飾器被調(diào)用了')
? ? ? ? print('請求路徑%s' % request.path)
? ? ? ? return func(request, *args, **kwargs)
? ? return wrapper
# 我們定義的類視圖
class DemoView(View):
? ? # 我們給get方法添加上裝飾器, 然后執(zhí)行.
? ? @my_decorator
? ? def get(self, request):
? ? ? ? print('get方法')
? ? ? ? return HttpResponse('ok')
? ? # 類視圖里面的對象方法: post方法
? ? def post(self, request):
? ? ? ? print('post方法')
? ? ? ? return HttpResponse('ok')
? 把一個修飾函數(shù)的裝飾器作用在類上的幾種方法
? 1>在URL配置中裝飾
? ? ? url配置中使用 as_view( ) 函數(shù)返回一個view( ) 函數(shù)
? ? ? 換句話說在調(diào)用類視圖之前疗涉,必然會調(diào)用 view( ) 這個函數(shù)拿霉,所以把裝飾器作用到 view( ) 函數(shù)上
# 導(dǎo)入views.py視圖文件
from . import views
urlpatterns = [
? ? # 我們在路由部分, 把定義好的裝飾器添加到當(dāng)前的函數(shù)上
? ? # 這里需要注意: as_view() 會返回一個 view() 函數(shù)
? ? # 所以我們把裝飾器添加到view()函數(shù)上
? ? url(r'^demo/$', views.my_decorate(views.DemoView.as_view()))
]
? ? ? 這種方式的弊端
? ? ? ? ? ■ 此種方式最簡單,但因裝飾行為被放置到了url配置中咱扣,單看視圖的時候無法知道此視圖還被添加了裝飾器绽淘,不利于代碼的完整性,不建議使用闹伪。
? ? ? ? ? ■ 此種方式會為類視圖中的所有請求方法都加上裝飾器行為(因為是在視圖入口處沪铭,分發(fā)請求方式前)。
? 2>在類視圖中使用裝飾器
? ? ? 因為裝飾器的內(nèi)函數(shù)第一個參數(shù)是request偏瓤,而類視圖里面的函數(shù)第一個參數(shù)是self杀怠,第二個參數(shù)才是request。所以我們不能夠直接給類視圖中的函數(shù)添加裝飾器厅克,否則傳入的參數(shù)會出現(xiàn)錯誤
? ? ? 解決方法:
? 由上面的案例我們可知: 我們定義的裝飾器不能夠直接使用到類視圖的方法上.
? 所以我們需要把我們定義的裝飾器進行轉(zhuǎn)化, 轉(zhuǎn)化為能夠被類視圖中函數(shù)使用的裝飾器
? 轉(zhuǎn)化的方法需要導(dǎo)入:
? from django.utils.decorators import method_decorator
? 導(dǎo)入進來之后, 我們需要把自定義的裝飾器用這個方法包裹住轉(zhuǎn)化, 例如:
? @method_decorator(自定義裝飾器)
? ? ? 第一種解決方式:
? 在類視圖中使用為函數(shù)視圖準(zhǔn)備的裝飾器時赔退,不能直接添加裝飾器
? 需要使用method_decorator將其轉(zhuǎn)換為適用于類視圖方法的裝飾器。
from django.views.generic import View
# 導(dǎo)入轉(zhuǎn)換的裝飾器方法:
from django.utils.decorators import method_decorator
# 為特定請求方法添加裝飾器
class DemoView(View):
? ? # 使用轉(zhuǎn)換的方法將裝飾器轉(zhuǎn)化:
? ? @method_decorator(my_decorator)
? ? def get(self, request):
? ? ? ? print('get方法')
? ? ? ? return HttpResponse('ok')
? ? def post(self, request):
? ? ? ? print('post方法')
? ? ? ? return HttpResponse('ok')
? 問題:
? ? ? 雖然上面的方式可以解決類視圖添加裝飾器問題, 但是我們這種是給單個函數(shù)添加的, 而不是類視圖中的所有函數(shù)
? ? ? 第二種解決方式:
? 重寫dispatch方法已骇,給dispatch方法加裝飾器
? 使用method_decorator將其轉(zhuǎn)換為適用于類視圖方法的裝飾器
from django.views.generic import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
# 自定義的裝飾器方法
def my_decorator(func):
? ? def wrapper(request, *args, **kwargs):
? ? ? ? print('自定義裝飾器被調(diào)用了')
? ? ? ? print('請求的路徑:%s' % request.path)
? ? ? ? return func(request, *args, **kwargs)
? ? return wrapper
# 類視圖
class DemoView(View):
? ? # 重寫父類的dispatch方法, 因為這個方法被 as_view() 中的 view() 調(diào)用
? ? ? # 所以我們對這個方法添加裝飾器, 也就相當(dāng)于對整個類視圖的方法添加裝飾器.
? ? ? @method_decorator(my_decorator)
? ? def dispatch(self, request, *args, **kwargs):
? ? ? ? # 重寫父類的這個方法我們不會修改它的任何參數(shù), 所以我們直接調(diào)用父類的這個方法即可
? ? ? ? # 它里面的參數(shù)我們也不動它, 直接還傳遞過去.
? ? ? ? return super().dispatch(request, *args, **kwargs)
? ? def get(self, request):
? ? ? ? print('get')
? ? ? ? return HttpResponse('getfunc ok')
? ? def post(self, request):
? ? ? ? print('post')
? ? ? ? return HttpResponse('postfunc ok')
? ? ? 第三種解決方式:
? ? ? ? ? ■ method_decorator( )方法直接裝飾到類上去离钝,使當(dāng)前的視圖類中的某一個函數(shù)添加裝飾器方法
from django.views.generic import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
def my_decorator(func):
? ? def wrapper(request, *args, **kwargs):
? ? ? ? print('自定義裝飾器被調(diào)用了')
? ? ? ? print('請求的路徑:%s' % request.path)
? ? ? ? return func(request, *args, **kwargs)
? ? return wrapper
"""
類視圖
給類視圖增加上@method_decorator方法
增加上之后,并不能夠給其中的某一個函數(shù)增加上裝飾器
所以我們需要給method_decator配置第二個參數(shù)
第二個參數(shù)就是類中某一個函數(shù)的名稱褪储,意味著給當(dāng)前這個函數(shù)增加上裝飾器
"""
@method_decorator(my_decorator, name='get')
class DemoView(View):
? ? @method_decorator(my_decorator)
? ? def dispatch(self, request, *args, **kwargs):
? ? ? ? return super().dispatch(request, *args, **kwargs)
? ? def get(self, request):
? ? ? ? print('get')
? ? ? ? return HttpResponse('getfunc ok')
? ? def post(self, request):
? ? ? ? print('post')
? ? ? ? return HttpResponse('postfunc ok')
? ? ? 第四種解決方式:
? ? ? ? ? ■ 使用@method_decorator在類視圖的位置給'dispatch'方法添加裝飾器
from django.views.generic import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
def my_decorator(func):
? ? def wrapper(request, *args, **kwargs):
? ? ? ? print('自定義裝飾器被調(diào)用了')
? ? ? ? print('請求的路徑:%s' % request.path)
? ? ? ? return func(request, *args, **kwargs)
? ? return wrapper
# 類視圖
# 因為我們可以直接給dispatch方法添加裝飾器,意味著, 我們內(nèi)部不用重寫dispatch方法了.
@method_decorator(my_decorator, name='dispatch')
class DemoView(View):
? ? def get(self, request):
? ? ? ? print('get')
? ? ? ? return HttpResponse('getfunc ok')
? ? def post(self, request):
? ? ? ? print('post')
? ? ? ? return HttpResponse('postfunc ok')
? 3>method_decorator裝飾器的作用
? ? ? 為函數(shù)視圖準(zhǔn)備的裝飾器卵渴,其被調(diào)用時,第一個參數(shù)用于接收request對象
def my_decorate(func):
? ? def wrapper(request, *args, **kwargs):? # 第一個參數(shù)request對象
? ? ? ? ...代碼省略...
? ? ? ? return func(request, *args, **kwargs)
? ? return wrapper
? ? ? 而類視圖中請求方法被調(diào)用時鲤竹,傳入的第一個參數(shù)不是request對象浪读,而是self 視圖對象本身,第二個位置參數(shù)才是request對象
class DemoView(View):
? ? def dispatch(self, request, *args, **kwargs):
? ? ? ? ...代碼省略...
? ? def get(self, request):
? ? ? ? ...代碼省略...
? ? ? 所以如果直接將用于函數(shù)視圖的裝飾器裝飾類視圖方法辛藻,會導(dǎo)致參數(shù)傳遞出現(xiàn)問題碘橘。
? ? ? method_decorator裝飾器的作用是為函數(shù)視圖裝飾器補充第一個self參數(shù),以適配類視圖方法吱肌。
? 4>類視圖使用裝飾器的最終方法
? ? ? 將裝飾器本身改為可以適配類視圖方法
def my_decorator(func):
? ? def wrapper(self, request, *args, **kwargs):? # 此處增加了self
? ? ? ? print('自定義裝飾器被調(diào)用了')
? ? ? ? print('請求路徑%s' % request.path)
? ? ? ? return func(self, request, *args, **kwargs)? # 此處增加了self
? ? return wrapper
<5>構(gòu)造Mixin擴展類
? 1>給類視圖所有方法添加裝飾器有兩種
? ? ? 修改裝飾器接收到的參數(shù)
? ? ? ? ? ■ 即把裝飾器的第一個參數(shù)修改為self痘拆,使類中所有的方法都可以直接添加上裝飾器
? ? ? 修改類視圖中所有函數(shù)都會調(diào)用的 as_view() 方法
? ? ? ? ? ■ 或者是 dispatch方法, 因為dispatch方法在 as_view() 方法內(nèi)部,故我們這里不討論dispatch
? 2>使用類擴展的形式給當(dāng)前的類視圖添加裝飾器
? ? ? 也就是說,把裝飾器加在類擴展中氮墨,在類視圖的父類中重寫as_view()方法纺蛆,并繼承自View類的as_view()方法
? ? ? View類的as_view()方法返回了view(),給view()添加裝飾器
? ? ? 那么我們可以繼續(xù)思考
類視圖 —————> 繼承自View類 (包含有as_view函數(shù))
? ? ? 如果我們在整個繼承的過程中添加一步, 例如:
類視圖 ———> 繼承自額外擴展的類 ———> 繼承自View類(包含有as_view函數(shù))
? ? ? 在額外擴展的類中重寫 as_view( ) 方法规揪,并且對 as_view( ) 方法添加裝飾器桥氏,類視圖中的所有方法都會被裝飾器裝飾
? ? ? ? ? ■ 第一步:創(chuàng)建一個擴展類,在擴展類中重寫 as_view 方法猛铅,并且給父類傳過來的view方法添加裝飾器
? ? ? ? ? ■ 第二步:讓類視圖繼承自擴展類
? ? ? 第一步代碼:
# 定義一個新的擴展類,讓該類繼承自View類
class BaseView(View):
? ? # 在擴展類中,重寫View類的 as_view 方法, 并且對該方法添加裝飾器
? ? @classmethod
? ? def as_view(cls, *args, **kwargs):
? ? ? ? # 重寫之后, 不對該方法做其他額外操作,所以我們重新調(diào)用父類的該方法
? ? ? ? view = super().as_view(*args, **kwargs)
? ? ? ? # 對父類傳過來的view方法添加裝飾器
? ? ? ? view = my_decorator(view)
? ? ? ? return view
? ? ? 第二步代碼:
# 讓我們的類視圖繼承自擴展類
class DemoView(BaseView):
? ? def get(self, request):
? ? ? ? print('get')
? ? ? ? return HttpResponse('get func')
? ? def post(self, request):
? ? ? ? print('post')
? ? ? ? return HttpResponse('post func')
? 結(jié)論:
? ? ? 經(jīng)過中間一層額外擴展類的裝飾過濾字支,我們原來的DemoView中的所有視圖方法是能夠經(jīng)過裝飾器的
? 3>使用多個類擴展的形式給當(dāng)前的類視圖添加多個裝飾器
? ? ? 那么我們可以繼續(xù)思考:
類視圖 —————> 繼承自View類 (包含有as_view函數(shù))
類視圖 ———> 繼承自額外擴展的類1 ---> 繼承自額外擴展的類2 ———> 繼承自View類(包含有as_view函數(shù))
? ? ? 延伸:
我們定義兩個擴展類, 并且重寫兩次 as_view 方法, 來看看會發(fā)生什么 :
from django.views.generic import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
# 定義的第一個裝飾器:
def my_decorator(func):
? ? def wrapper(request, *args, **kwargs):
? ? ? ? print('自定義裝飾器被調(diào)用了')
? ? ? ? print('請求的路徑:%s' % request.path)
? ? ? ? return func(request, *args, **kwargs)
? ? return wrapper
# 定義的第二個裝飾器:
def my_decorator2(func):
? ? def wrapper(request, *args, **kwargs):
? ? ? ? print('自定義裝飾器被調(diào)用了22222')
? ? ? ? print('請求的路徑:%s' % request.path)
? ? ? ? return func(request, *args, **kwargs)
? ? return wrapper
# 額外增加的第一個擴展類
class BaseView(View):
? ? # 第一次重寫父類中的as_view方法
? ? @classmethod
? ? def as_view(cls, *args, **kwargs):
? ? ? ? view = super().as_view(*args, **kwargs)
? ? ? ? # 對獲取的view第一次添加裝飾器
? ? ? ? view = my_decorator(view)
? ? ? ? return view
# 額外增加的第二個擴展類
class Base2View(View):
? ? # 第二次重寫父類中的as_view方法
? ? @classmethod
? ? def as_view(cls, **initkwargs):
? ? ? ? view = super().as_view(**initkwargs)
? ? ? ? # 對獲取的view進行第二次添加裝飾器
? ? ? ? view = my_decorator2(view)
? ? ? ? return view
# 我們定義的類視圖, 繼承自兩個額外增加的類
class DemoView(BaseView, Base2View):
? ? # 類視圖中的get方法
? ? def get(self, request):
? ? ? ? print('get')
? ? ? ? return HttpResponse('get func')
? ? # 類視圖中的post方法
? ? def post(self, request):
? ? ? ? print('post')
? ? ? ? return HttpResponse('post func')
? 那么我們來看一下同時調(diào)用兩個裝飾器是怎樣實現(xiàn)的:
? 說明:
? ? ? 如果兩個擴展類的父類相同:則兩個父類都會調(diào)用
? ? ? 如果兩個擴展類的父類不同:則只會調(diào)用第一個父類
? 綜上,我們可以把代碼變成這個樣子:
? ? ? 為什么BaseView類和Base2View類都繼承自object還能重寫as_view()?
? ? ? ? ? ■ 當(dāng)程序運行到BaseView類的view = super().as_view(*args, **kwargs)時堕伪,會在父類中查找as_view()揖庄,如果沒找到會去Base2View類中以及父類中查找,如果還沒有會去View類中查找欠雌,再沒有就會報錯了抠艾。
# 第一個擴展類, 讓他繼承自object
class BaseView(object):
? ? @classmethod
? ? def as_view(cls, *args, **kwargs):
? ? ? ? view = super().as_view(*args, **kwargs)
? ? ? ? view = my_decorator(view)
? ? ? ? return view
# 第二個擴展類,讓他繼承自object
class Base2View(object):
? ? @classmethod
? ? def as_view(cls, *args, **kwargs):
? ? ? ? view = super().as_view(*args, **kwargs)
? ? ? ? view = my_decorator2(view)
? ? ? ? return view
# 類視圖, 讓他除了繼承自這兩個父類外, 最后繼承View類.
class DemoView(BaseView, Base2View, View):
? ? def get(self, request):
? ? ? ? print('get方法')
? ? ? ? return HttpResponse('ok')
? ? def post(self, request):
? ? ? ? print('post方法')
? ? ? ? return HttpResponse('ok')
? 說明:
? ? ? 因為都是繼承自object,所以擴展類中的super.as_view都會去找其他的父類依次執(zhí)行桨昙,最終都會執(zhí)行到View這個類這里,所以肯定會執(zhí)行View中的as_view方法
? ? ? 使用Mixin擴展類腌歉,也會為類視圖的所有請求方法都添加裝飾行為蛙酪。
6.中間件
? 中間件類似請求鉤子
? Django中的中間件是一個輕量級、底層的插件系統(tǒng)翘盖,可以介入Django的請求和響應(yīng)處理過程桂塞,修改Django的輸入或輸出。中間件的設(shè)計為開發(fā)者提供了一種無侵入式的開發(fā)方式馍驯,增強了Django框架的健壯性阁危。
? 我們可以使用中間件,在Django處理視圖的不同階段對輸入或輸出進行干預(yù)汰瘫。
<1>中間件的定義方法
? 1>定義中間件的步驟
? ? ? 在一個子應(yīng)用中創(chuàng)建一個中間件文件狂打,例如: middleware(別的名字也可以)
? ? ? ? ? ■ 在中間件文件中定義一個中間件工廠函數(shù),然后返回一個可以調(diào)用的中間件混弥。
? ? ? ? ? ■ 中間件工廠函數(shù)需要接收一個可以調(diào)用的get_response對象趴乡。這個函數(shù)接收的參數(shù)只能是get_response
? ? ? ? ? ■ 返回的中間件也是一個可以被調(diào)用的對象,并且像視圖一樣需要接收一個request對象參數(shù)蝗拿,返回一個response對象晾捏。
? ? ? 在setting.py文件的MIDDLEWARE部分注冊添加
? ? ? 在調(diào)用視圖時,便會調(diào)用中間件了
? 2>中間件模板
? ? ?
def simple_middleware(get_response):
? ? # 此處編寫的代碼僅在Django第一次配置和初始化的時候執(zhí)行一次哀托。
? ? def middleware(request):
? ? ? ? # 此處編寫的代碼會在每個請求處理視圖前被調(diào)用惦辛。
? ? ? ? response = get_response(request)
? ? ? ? # 此處編寫的代碼會在每個請求處理視圖之后被調(diào)用。
? ? ? ? return response
? ? return middleware
? 3>定義中間件的案例
? ? ? 在子應(yīng)用users中新建一個中間件文件middleware.py
? ? ? ? ? ■ 在中間件文件中定義一個中間件工廠函數(shù)
def my_middleware(get_response):
? ? print('init 被調(diào)用')
? ? def middleware(request):
? ? ? ? print('before request 被調(diào)用')
? ? ? ? response = get_response(request)
? ? ? ? print('after response 被調(diào)用')
? ? ? ? return response
? ? return middleware
? ? ? 定義好中間件后仓手,需要在工程同名應(yīng)用下的settings.py文件中添加注冊中間件
? ? ? ? ? ■ 子應(yīng)用名.中間件文件名.中間件工廠函數(shù)名
? ? ? ? ? ■ 'users.middleware.my_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',
? ? 'users.middleware.my_middleware',? # 添加中間件
]
? ? ? 定義一個視圖進行測試
def demo_view(request):
? ? print('view 視圖被調(diào)用')
? ? return HttpResponse('OK')
? 注意:Django運行在調(diào)試模式下胖齐,中間件init部分有可能被調(diào)用兩次,關(guān)閉調(diào)試模式只會運行一次
<2>多個中間件的執(zhí)行順序
? 在請求視圖被處理前俗或,中間件由上至下依次執(zhí)行
? 在請求視圖被處理后市怎,中間件由下至上依次執(zhí)行
? 1>定義兩個中間件
def my_middleware(get_response):
? ? print('init 被調(diào)用')
? ? def middleware(request):
? ? ? ? print('before request 被調(diào)用')
? ? ? ? response = get_response(request)
? ? ? ? print('after response 被調(diào)用')
? ? ? ? return response
? ? return middleware
def my_middleware2(get_response):
? ? print('init2 被調(diào)用')
? ? def middleware(request):
? ? ? ? print('before request 2 被調(diào)用')
? ? ? ? response = get_response(request)
? ? ? ? print('after response 2 被調(diào)用')
? ? ? ? return response
? ? return middleware
? 2>注冊添加兩個中間件
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',
? ? 'users.middleware.my_middleware',? # 添加
? ? 'users.middleware.my_middleware2',? # 添加
]
? 3>執(zhí)行結(jié)果
init2 被調(diào)用
init 被調(diào)用
before request 被調(diào)用
before request 2 被調(diào)用
view 視圖被調(diào)用
after response 2 被調(diào)用
after response 被調(diào)用