程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Django從入門到放棄三 -- cookie,session,cbv加裝飾器,ajax,django中間件,redis緩存等

編輯:Python

學習地址:Django從入門到放棄 - 劉清政 - 博客園

一、CBV寫法 ( class base views )  一般指在views.py文件中定義的各種類

       FBV ( function base views ) 一般指在views.py文件中定義的各種函數

1.1、CBV寫法例子:

   CBV源碼分析:views.Index.as_view() 具體是怎麼實現的

   as_view 是一個classmethod方法,會將Index類傳進去,

   as_view 函數內有個閉包函數 view ,view函數內會實例化Index類(self = cls(**initkwargs)),拿到一個Index類對象。

   調用Index類對象的self.dispatch方法,如果Index類裡有這個方法就調用,否則就調用基類(也就是View類),

   View類 的 dispatch方法裡面定義了請求方法:http_method_names = ['get', 'post', 'put', 'patch']這個方法就是對應了各種不同的HTTP請求方式。然後通過一個getattr反射方法拿到"Index類對象"下"get"方法返回值(就是自己寫的get函數的內存地址)。然後加括號執行這個"get"函數,並拿到"get"函數的返回值,然後返回。

    def dispatch(self, request, *args, **kwargs):
        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)

  寫一個CBV的例子:

在urls.py文件中:
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# CBV測試,訪問views.py文件下的CBTtest類,需要使用"CBTtest.as_view()"的方法
path('test/', views.CBTtest.as_view()), # as_view()拿到的是CBTtest類裡面某個函數的名稱[詳細信息查看View類裡面的as_view函數,最終返回一個get|post函數的內存地址]以及一個request對象。
]
# 在views.py文件中:
from django.views import View # View類
class CBTtest(View):
# GET請求訪問這個函數
def get(self,request):
print(request.method)
return render(request, 'test.html')
# POST請求訪問這個函數
def post(self,request):
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'szq' and pwd == '123':
return HttpResponse('登錄成功')
else:
return render(request, 'test.html')
# test.html
<form action="" method="post">
<p>用戶名:<input type="text" name="name"></p>
<p>密碼:<input type="text" name="pwd"></p>
<p><input type="submit" value="提交"></p>
</form>

1.2、CBV寫法總結

    1、定義請求方式的函數:函數名必須為父類View裡面定義的http_method_names的名稱。
    2、http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    3、根據前台發過來的請求的方式"GET或者POST"請求,就會響應對應名為"GET或者POST"的函數(每次響應一個函數)。
    4、"GET或者POST"的函數寫法與FBV的函數寫法一致。
    5、在URL裡面配置 "path('test/', views.CBTtest.as_view())"

二、cookie的使用

2.1、什麼是cookie

    Cookie是key-value鍵值對,存放在客戶端,類似於一個python中的字典。隨著服務器端的響應發送給客戶端浏覽器。然後客戶端浏覽器會把Cookie保存起來,當下一次再訪問服務器時把Cookie再發送給服務器。 Cookie是由服務器創建,然後通過響應發送給客戶端的一個鍵值對。客戶端會保存Cookie,並會標注出Cookie的來源(哪個服務器的Cookie)。當客戶端向服務器發出請求時會把所有這個服務器Cookie包含在請求中發送給服務器,這樣服務器就可以識別客戶端了!

  Cookie大小上限為4KB,一個服務器最多在客戶端浏覽器上保存20個Cookie,一個浏覽器最多保存300個Cookie。

2.2、Django中操作Cookie

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

  參數:
     default: 默認值
     salt: 加密鹽
     max_age: 後台控制過期時間

   2.2.1、小例子  --  使用cookie判斷用戶的登錄狀態,判斷用戶是否需要重新登錄


# 在views.py文件中
# 寫一個功能,如果用戶登錄時cookie數據驗證失敗則重新登錄一次。
def test_cookie_login(request):
if request.method == 'POST':
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'szq' and pwd == '123':
obj=redirect('/index/')
obj.set_cookie('is_login',True)
obj.set_cookie('name',name)
return obj
return render(request,'test.html')
# index函數通過判斷用戶的cookie是否在客戶端浏覽器上存在(沒有過期),用戶是否需要再次登錄。
def index(request):
print(request.COOKIES)
is_login=request.COOKIES.get('is_login')
name=request.COOKIES.get('name')
if is_login: # 如果cookie驗證成功就返回首頁
return render(request,'index.html',{'name':name})
else: # 否則就返回登錄頁面
return redirect('/test_cookie_login/')
# 在urls.py文件中
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# cookie與session
path('test_cookie_login/', views.test_cookie_login),
re_path('^index/$', views.index ),
]

   2.2.2、小例子  --  用戶當前訪問的頁面(/aaa/),在使用登錄裝飾器後(用戶登錄後),這裡重定向的值是寫死的,依然返回用戶想要登錄的頁面(/aaa/)

# 在views.py文件中
# 用戶登錄裝飾器
def login_auth(func):
def inner(request,*args,**kwargs):
next_url=request.get_full_path() # 用戶當前訪問的頁面(/test_order/),在使用登錄裝飾器後(用戶登錄後),依然返回用戶想要登錄的頁面(/test_order/)
is_login = request.COOKIES.get('is_login')
if is_login:
return func(request,*args,**kwargs)
else:
# 此時用戶訪問的地址格式為:http://127.0.0.1:8000/test_cookie_login/?next=/test_order/
return redirect('/test_cookie_login/?next=%s' %next_url) # 記錄下用戶當前訪問的頁面,傳值給"next"
return inner
# 寫一個功能,如果用戶登錄時cookie數據驗證失敗則重新登錄一次。
def test_cookie_login(request):
if request.method == 'POST':
url = request.GET.get('next') # 通過request獲取到數據"?next=/test_order/",拿到關鍵字'next'對應的數據"/test_order/"
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'szq' and pwd == '123':
obj=redirect(url) # 用戶在登錄成功後,直接重定向到"/test_order/"目錄下(這個目錄是用戶一開始就想要訪問的目錄)
obj.set_cookie('is_login',True)
obj.set_cookie('name',name)
return obj
return render(request,'test.html')
# 默認網站的首頁,訪問的時候需要先登錄
@login_auth # index=login_auth(index)
def index(request):
name=request.COOKIES.get('name')
return render(request,'index.html',{'name':name}) # index.html為網站首頁
# 用戶當前訪問的頁面(/test_order/),在使用登錄裝飾器後(用戶登錄後),依然返回用戶想要登錄的頁面(/test_order/)
@login_auth
def test_order(request):
return render(request,'order.html') # order.html 為購物車頁面
# 在urls.py文件中
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# re_path('^index/$', views.index ),
# cookie與session
re_path('test_cookie_login/', views.test_cookie_login),
re_path('^index/$', views.index ),
re_path('^test_order/$', views.test_order ),
]

 2.2.3、cookie的其他用法

    obj.set_cookie('is_login',True,max_age=5)     :設置cookie的緩存時間/超時時間(s)
    obj.set_cookie('is_login',True,path='/index/')   :表示指定cookie的路徑(訪問此路徑時會產生cookie),非此路徑將不產生cookie。
    obj.delete_cookie("is_login")                            :刪除一個cookie(指定cookie的"key")

三、session的使用

3.1、什麼是session?

      Session是key-value鍵值對,存放在服務端,Cookie雖然在一定程度上解決了“保持狀態”的需求,但是由於Cookie本身最大支持4096字節,以及Cookie本身保存在客戶端,可能被攔截或竊取,因此就需要有一種新的東西,它能支持更多的字節,並且他保存在服務器,有較高的安全性。這就是Session。

3.2、Django中Session相關方法

    3.2.1、Session的生成

def test_session(request):
# 客戶端A,生成A_session
request.session['username']='szq' # 生成一個session_key與session_data
# 客戶端B,生成B_session
request.session['age'] = '18' # 生成一個session_key與session_data
request.session['sex']='male' # 生成一個session_data與request.session['age'] = '18'公用一個session_key
'''
request.session['username']='szq' # 執行的操作的有:
1.生成隨機字符串session_key(這個字符串是返回給客戶端的,在不同的客戶端會生成不同的生成隨機字符串session_key)
# 在數據庫內對應的就是:session_key(一個session_key裡面可以包含多個字典)
2.在django表裡生成一條記錄
# 在數據庫內對應的就是:session_data(一個字典類型的數據),數據是加密後的{'username':'szq'}
4.把(session_key)隨機字符串返回到客戶端浏覽器上
'''
return HttpResponse('ok')

    session在數據庫內存在的格式如下:默認存放在數據庫的django_session表

    session_key:一串隨機字符串,這個字符串是返回給客戶端的

    session_data:一個字典類型的數據,數據是加密後的 ["username"]="sudada"

    以上2者的對應關系:session_key:session_data

    3.2.1、Session的操作(增刪改查等操作)


# 設置session['username']的值為123456
request.session['username'] = 123456
# 獲取session['username']的值"szq"
request.session['username']
# 刪除session['username']這個key及對應的value
del request.session['username']
# 獲取"session['key']"的值:dict_keys(['sex', 'username', 'age'])
request.session.keys()
# 獲取"session['value']"的值:dict_values(['male', 'szq', '18'])
request.session.values()
# 獲取"session['key:value']"的值:dict_items([('sex', 'male'), ('username', 'szq'), ('age', '18')])
request.session.items()
# 獲取當前客戶端連接服務器會話session的key"of1op71gr1qaf9sj8ey31gmeehlkrynk"
request.session.session_key
# 檢查會話session的key在數據庫中是否存在
request.session.exists("0ynniz04nh3gd7plli88xyyhztu7hony")
#刪除當前會話的所有Session數據(只刪數據庫,不刪除浏覽器的cookie)
request.session.delete()
# 刪除當前的會話數據並刪除會話的Cookie(數據庫和cookie都刪)
request.session.flush()
# 這用於確保前面的會話數據不可以再次被用戶的浏覽器訪問
# 例如,django.contrib.auth.logout() 函數中就會調用它。
# 設置會話Session和Cookie的超時時間
request.session.set_expiry(value)
* 如果value是個整數,session會在些秒數後失效,再次訪問時會生成一個新的session
* 如果value是個datatime或timedelta,session就會在這個時間後失效,再次訪問時會生成一個新的session
* 如果value是0,用戶關閉浏覽器session就會失效,再次訪問時會生成一個新的session
* 如果value是None,session會依賴全局session失效策略。

3.3、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' # 引擎
其他公用設置項:放在setting.py文件內
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,默認修改之後才保存(默認)

四、cbv加裝飾器

   1.在CBV內部的函數加裝飾器

   2.直接在類上使用裝飾器(需要指定"實例化對象"的名稱)

# 在urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# CBV裝飾器測試
path('test_cbv/', views.CBTtest.as_view()),
re_path('test_cookie_login/', views.test_cookie_login),
]
# 在views.py文件中
# 用戶登錄裝飾器
def login_auth(func):
def inner(request, *args, **kwargs):
next_url = request.get_full_path() # 用戶當前訪問的頁面(/test_cbv/),在使用登錄裝飾器後(用戶登錄後),依然返回用戶想要登錄的頁面(/test_cbv/)
is_login = request.COOKIES.get('is_login')
if is_login:
return func(request, *args, **kwargs)
else:
# 此時用戶訪問的地址格式為:http://127.0.0.1:8000/test_cookie_login/?next=/test_cbv/
return redirect('/test_cookie_login/?next=%s' % next_url) # 記錄下用戶當前訪問的頁面,傳值給"next"
return inner
# 登錄接口
def test_cookie_login(request):
if request.method == 'POST':
url = request.GET.get('next') # 通過request獲取到數據"?next=/test_cbv/",拿到關鍵字'next'對應的數據"/test_cbv/"
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'szq' and pwd == '123':
obj = redirect(url) # 用戶在登錄成功後,直接重定向到"/test_cbv/"目錄下(這個目錄是用戶一開始就想要訪問的目錄)
obj.set_cookie('is_login',True)
obj.set_cookie('name',name)
return obj
return render(request,'login.html')
# CBV裝飾器的用法及寫法--方式一(在類實例化的函數裡使用)
from django.views import View
from django.utils.decorators import method_decorator
class CBTtest(View):
# GET請求訪問這個函數
@method_decorator(login_auth)
def get(self,request):
print(request.method)
return render(request, 'login.html')
# POST請求訪問這個函數
@method_decorator(login_auth)
def post(self,request):
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'szq' and pwd == '123':
return HttpResponse('登錄成功')
else:
return render(request, 'login.html')
# CBV裝飾器的用法方式二(在類上面印有)
@method_decorator(login_auth,name='get')
@method_decorator(login_auth,name='post')
class CBTtest(View):
# GET請求訪問這個函數
def get(self,request):
print(request.method)
return render(request, 'login.html')
# POST請求訪問這個函數
def post(self,request):
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'szq' and pwd == '123':
return HttpResponse('登錄成功')
else:
return render(request, 'login.html')

五、ajax提交數據,提交json格式數據

  5.1、什麼是ajax?

   1、ajax(Asynchronous Javascript And XML)翻譯成中文就是“異步Javascript和XML”。即使用Javascript語言與服務器進行異步交互,傳輸的數據為XML(當然,傳輸的數據不只是XML,現在更多使用json數據)。
      同步交互:客戶端發出一個請求後,需要等待服務器響應結束後,才能發出第二個請求;
      異步交互:客戶端發出一個請求後,無需等待服務器響應結束,就可以發出第二個請求。
2、ajax除了異步的特點外,還有一個就是:浏覽器頁面局部刷新;(這一特點給用戶的感受是在不知不覺中完成請求和響應過程)

5.2、ajax應用場景?

5.3、ajax的優點

    1、ajax使用Javascript技術向服務器發送異步請求
    2、ajax無須刷新整個頁面

5.4、ajax使用例子1 --  使用ajax(GET|POST)請求一個登陸界面

        後端獲取數據時,使用request.GET.get或者request.POST.get的方式獲取數據。

# 在urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# 測試AJAX
re_path('test_ajax/', views.test_ajax),
re_path('ajax/', views.ajax),
re_path('index/', views.index),
]
# views.py文件中
import json
def test_ajax(request):
return render(request,'ajax.html')
def ajax(request):
bak_msg={'user':None,'msg':'用戶名密碼錯誤'}
# 前端ajax使用get或者post方式請求數據,後端使用request.GET|POST.get的方式獲取數據
name=request.GET.get('name')
pwd=request.GET.get('pwd')
if name == 'szq' and pwd == '123':
bak_msg['user']=name
bak_msg['msg']='登錄成功'
return HttpResponse(json.dumps(bak_msg))
def index(request):
return render(request,'index.html')

   前端ajax.html文件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="/static/jquery-3.3.1.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post">
<p>用戶名:<input type="text" name="name" id="name"></p>
<p>密碼:<input type="text" name="pwd"></p>
{# <p><input type="submit" value="提交"></p>#}
</form>
<button id="btnl">提交</button>
{#針對服務端,是一個異步的請求#}
<script>
$('#btnl').click(function () {
var name=$('#name').val() {# 獲取用戶名信息 #}
var pwd=$('[name="pwd"]').val() {# 獲取密碼信息 #}
$.ajax({
url: '/ajax/', {# 請求到服務器的URL #}
type: 'get', {# 給服務器發送的請求方式,後端取數據需要從request.GET(POST)裡面取數據 #}
data:{'name':name,'pwd':pwd}, {# 請求服務器時攜帶的信息(用戶名和密碼) #}
success: function (data) { {# data拿到的是服務端"ajax"請求的返回結果(是一個異步的返回結果) #}
{# alert(data) {# 拿到服務器數據後的返回值 #}
var msg=JSON.parse(data); {# 拿到後端返回的json格式的數據,並轉譯 #}
if (msg.user){ {# 如果用戶存在,表示登錄成功 #}
location.href='/index/' {# 登錄成功後,重定向到'/index/'頁面 #}
}else {
alert(msg.msg) {# 登錄失敗返回的結果 #}
}
}
})
})
</script>
</body>
</html>

5.4、ajax使用例子2 --  使用ajax(JSON格式)請求一個登陸界面

        後端獲取數據時,使用request.body的方式獲取數據。

        後端獲取數據時,需要將獲取到的二進制字符串轉譯。

        後端獲取數據時,需要把json格式的字符串轉譯成Python格式的。

# 在urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# 測試AJAX
re_path('test_ajax/', views.test_ajax),
re_path('ajax/', views.ajax),
re_path('index/', views.index),
]
# views.py文件中
import json
def test_ajax(request):
return render(request,'ajax.html')
def ajax(request):
print(request.body)
# b'{"name":"szq","pwd":"123"}' 獲取到前端請求的數據(包含用戶名和密碼),是一個二進制格式,需要轉譯
# 這裡不是通過request.POST獲取到數據了
res=request.body.decode('utf-8') # 把二進制格式的數據轉譯成正常
res_dic=json.loads(res) # 把json格式的數據轉換成python格式
bak_msg={'user':None,'msg':'用戶名密碼錯誤'}
if res_dic['name'] == 'szq' and res_dic['pwd'] == '123':
bak_msg['user']=res_dic['name']
bak_msg['msg']='登錄成功'
return HttpResponse(json.dumps(bak_msg))
def index(request):
return render(request,'index.html')

   前端ajax.html文件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="/static/jquery-3.3.1.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post">
<p>用戶名:<input type="text" name="name" id="name"></p>
<p>密碼:<input type="text" name="pwd"></p>
{# <p><input type="submit" value="提交"></p>#}
</form>
<button id="btnl">提交</button>
{#針對服務端,是一個異步的請求#}
<script>
$('#btnl').click(function () {
var name=$('#name').val() {# 獲取用戶名信息 #}
var pwd=$('[name="pwd"]').val() {# 獲取密碼信息 #}
$.ajax({
url: '/ajax/', {# 請求到服務器的URL #}
type: 'post', {# 給服務器發送的請求方式 #}
{# 如果需要向後台提交json格式的字符串時,需要指定:contentType:'application/json' #}
{# 當contentType為json格式的時候,後端取數據需要從request.body裡面取數據 #}
contentType:'application/json',
{# 請求服務器時攜帶的信息(用戶名和密碼)json格式的字符串 #}
{# 如果不加"JSON.stringify"那麼後端獲取到的數據格式就為b'name:szq,pwd:123',加上"JSON.stringify"之後,後端獲取到的數據格式就為b'{"name":"szq","pwd":"123"}'#}
data:JSON.stringify({'name':name,'pwd':pwd}),
success: function (data) { {# data拿到的是服務端"ajax"請求的返回結果(是一個異步的返回結果) #}
{#alert(data) {# 拿到服務器數據後的返回值 #}
var msg=JSON.parse(data); {# 拿到後端返回的json格式的數據,並轉譯 #}
if (msg.user){ {# 如果用戶存在,表示登錄成功 #}
location.href='/index/' {# 登錄成功後,重定向到'/index/'頁面 #}
}else {
alert(msg.msg) {# 登錄失敗返回的結果 #}
}
}
})
})
</script>
</body>
</html>

  5.5、Ajax---->服務器------>Ajax執行流程圖

六、基於form表單上傳文件

       前端form表單需要指定:enctype="multipart/form-data"

       後端獲取form表單上傳文件內容時,需要用"request.FILES.get"

# 在urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# from表單上傳文件
re_path('file_upload/', views.file_upload),
]
# 在views.py文件中
def file_upload(request):
if request.method == "POST":
# 上傳一個圖片時返回的數據(字典類型):<MultiValueDict: {'myfile': [<InMemoryUploadedFile: DDD.jpg (image/jpeg)>]}>
print(request.FILES)
# 查看這個字典類型數據裡面'myfile'這個key對應的value的值的類型:<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
print(type(request.FILES.get('myfile')))
# 查看源碼,InMemoryUploadedFile類
from django.core.files.uploadedfile import InMemoryUploadedFile
# 通過key拿到對應圖片的數據內容
myfile=request.FILES.get('myfile')
# 通過圖片的數據內容拿到圖片的文件名
file_name=myfile.name
print(file_name) # bbb.jpg
# 保存這個圖片到服務器
with open(file_name,'wb')as f:
for line in myfile: # 這裡循環myfile這個文件
f.write(line)
return HttpResponse('文件上傳成功')
return render(request,'file_upload.html')

  前端'file_upload.html'文件內容:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="/static/jquery-3.3.1.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data"> {# 使用form表單上傳數據時的固定用法 #}
<p>用戶名:<input type="text" name="name" id="name"></p>
<p>文件:<input type="file" name="myfile"></p>
<p><input type="submit" value="form表單提交"></p>
</form>
</body>
</html>

七、基於ajax上傳文件

       前端ajax:var formdata=new FormData

       後端獲取form表單上傳文件內容時,需要用"request.FILES.get"

# 在urls.py文件中
urlpatterns = [
path('admin/', admin.site.urls),
# from表單上傳文件
re_path('file_upload/', views.file_upload),
]
# 在views.py文件中
def file_upload(request):
if request.method == "POST":
# 上傳一個圖片時返回的數據(字典類型):<MultiValueDict: {'myfile': [<InMemoryUploadedFile: DDD.jpg (image/jpeg)>]}>
print(request.FILES)
# 查看這個字典類型數據裡面'myfile'這個key對應的value的值的類型:<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
print(type(request.FILES.get('myfile')))
# 查看源碼,InMemoryUploadedFile類
from django.core.files.uploadedfile import InMemoryUploadedFile
# 通過key拿到對應圖片的數據內容
myfile=request.FILES.get('myfile')
# 通過圖片的數據內容拿到圖片的文件名
file_name=myfile.name
print(file_name) # bbb.jpg
# 保存這個圖片到服務器
with open(file_name,'wb')as f:
for line in myfile: # 這裡循環myfile這個文件
f.write(line)
return HttpResponse('文件上傳成功')
return render(request,'file_upload.html')

 前端'file_upload.html'文件內容:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="/static/jquery-3.3.1.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data"> {# 使用form表單上傳數據時的固定用法 #}
<p>用戶名:<input type="text" name="name" id="name"></p>
<p>文件:<input type="file" name="myfile" id="myfile"></p>
</form>
<button id="btnl">ajax提交</button>
<script>
$('#btnl').click(function () {
var formdata=new FormData;
formdata.append('name',$("#name").val());
formdata.append('myfile',$("#myfile")[0].files[0]);
$.ajax({
url: '/file_upload/',
type: 'post',
contentType:false,
processData:false,
data:formdata,
success: function (data) {
alert(data)
}
})
})
</script>
</body>
</html>

八、django中間件

8.1、什麼是中間件?

       中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全局上改變django的輸入與輸出。因為改變的是全局,所以需要謹慎實用,用不好會影響到性能。

8.2、中間件有什麼用?  用戶所有的請求都會先經過中間件,然後到達URL路由層,用戶拿到數據的返回結果之後,也是先經過中間件(倒序),然後到達用戶。

       1、如果你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些都可以通過中間件來實現。
      2、可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實現。
      3、Django默認的中間件:(在django項目的settings模塊中,有一個 MIDDLEWARE_CLASSES 變量,其中每一個元素就是一個中間件,如下

# 每一個中間件都有具體的功能
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',
]

    4、當用戶發起請求的時候會依次經過所有的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。

8.3、自定義中間件

# 在settings.py文件中的MIDDLEWARE自定義中間件(app01目錄下的mytest文件)
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.mytest.MyMid1',
'app01.mytest.MyMid2',
]
# mytest.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.utils.deprecation import MiddlewareMixin
class MyMid1(MiddlewareMixin):
def process_request(self,request):
print('MyMid1 --- process_request')
# return HttpResponse('ok') # 這裡不能返回,返回的話,後面的request和response就不會執行了。
def process_response(self,request,response): # response是一個<class 'django.http.response.HttpResponse'>
print('MyMid1 --- process_response')
return response # 用戶請求後,必須要一個response數據返回
def process_view(self, request, callback, callback_args, callback_kwargs):
print('MyMid1 --- process_view')
class MyMid2(MiddlewareMixin):
def process_request(self,request):
print('MyMid2 --- process_request')
def process_response(self,request,response): # response是一個<class 'django.http.response.HttpResponse'>
print('MyMid2 --- process_response')
return response # 用戶請求後,必須要一個response數據返回
def process_view(self, request, callback, callback_args, callback_kwargs):
print('MyMid2 --- process_view')
# 返回結果就是中間件的執行順序
# MyMid1 --- process_request
# MyMid2 --- process_request
# MyMid1 --- process_view
# MyMid2 --- process_view
# MyMid2 --- process_response
# MyMid1 --- process_response

 下圖進行分析上面mytest.py文件的過程:

    當最後一個"URL請求"process_request到達路由關系映射之後,請求會去"路由控制"process_view,然後依次往下,到達"視圖函數",最後通過process_response依次返回到達用戶。

九、中間件應用場景
    9.1、做IP訪問頻率限制
    某些IP訪問服務器的頻率過高,進行攔截,比如限制每分鐘不能超過20次。

    9.2、URL訪問過濾
    如果用戶訪問的是login視圖(放過)
    如果訪問其他視圖,需要檢測是不是有session認證,已經有了放行,沒有返回login,這樣就省得在多個視圖函數上寫裝飾器了!

    9.3、作為延伸擴展內容,可以嘗試著讀一下以下兩個自帶的中間件:
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',

十、CSRF_TOKEN跨站請求偽造

使用Django自帶的中間件:'django.middleware.csrf.CsrfViewMiddleware' 可以防止跨站請求偽造。

原理:在前端HTML頁面加上"{% csrf_token %}",那麼在訪問的時候,會生成一個"<input type="hidden" name="csrfmiddlewaretoken" value="ODrQkcqNbmyYMREnjoCGUjwBuS0Cfz4m4VwzktZh1ryidjHUEFajE3BwdEgHpcWw">"隨機字符串,每次訪問生成的字符串都是不同的。前端HTML頁面在POST提交時會攜帶這個隨機字符串,然後提交到後端服務器,服務器會確認此次請求是否有這個隨機字符串,如果有則可以訪問,如果沒有訪問失敗。

十一、Django緩存機制

Django緩存機制 - 劉清政 - 博客園

11.1、redis緩存配置,視圖層面

# redis緩存,全局配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}, # 連接池大小100個
# "PASSWORD": "密碼",
}
}
}
# view視圖使用
import time
from django.views.decorators.cache import cache_page
@cache_page(5) # 緩存的時間5s
def index(request):
ctime=time.time()
return HttpResponse(ctime)
# 此時請求頁面,拿到一個數據,這個數據會在redis存5秒,之後會被刪除。此時可以使用命令"keys *" 查看到對應的緩存數據。
127.0.0.1:6379> keys *
1) ":1:views.decorators.cache.cache_page..GET.b8d12f80e31bbf250e9284534b72f75a.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC"
2) "name"
3) ":1:views.decorators.cache.cache_header..b8d12f80e31bbf250e9284534b72f75a.en-us.UTC"

11.2、django操作redis,使用連接池的方式

# redis緩存,全局配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}, # 連接池大小100個
# "PASSWORD": "密碼",
}
}
}
# view視圖使用
from django_redis import get_redis_connection
def test(request):
conn=get_redis_connection('default') # 對接全局配置(setting.py裡面的redis連接池)
name=conn.get("name") # 拿到redis裡面的"key"對應的"value"(字符串格式)
return HttpResponse(name)
# 返回數據
sudada


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved