Python 之 Django框架( Cookie和Session、Django中間件、AJAX、Django序列化)

  • 2020 年 9 月 16 日
  • 筆記

12.4 Cookie和Session

12.41 cookie

Cookie具體指的是一段小資訊,它是伺服器發送出來存儲在瀏覽器上的一組組鍵值對,下次訪問伺服器時瀏覽器會自動攜帶這些鍵值對,以便伺服器提取有用資訊

獲取Cookie:

request.COOKIES['login']                           #找不到時報錯
login_flag = request.COOKIES.get("login","")        #找不到時返回None
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
login_flag = request.get_signed_cookie("login",default="",salt="shanghais1hao")#找不到時返回默認值空

設置Cookie:給HttpResponse、render、redirect實例化的對象設置cookie

rep = HttpResponse("xxxx")
rep = render(request, "book_list.html")
rep = redirect('/book_list/')
​
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)
rep.set_signed_cookie("login", "ok", salt="shanghais1hao", max_age=None)

刪除Cookie:給HttpResponse、render、redirect實例化的對象刪除cookie

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

12.42 Session

Cookie彌補了HTTP無狀態的不足,讓伺服器知道來的人是「誰」;但是Cookie以文本的形式保存在本地,自身安全性較差;所以通過Cookie識別不同的用戶,對應的在Session里保存私密的資訊以及超過4096位元組的文本

獲取Session

request.session['k1']
request.session.get('k1',None)
login_flag = request.session.get("login")

設置Session

request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在則不設置,不存在則設置
request.session["login"] = "OK"
request.session["user"] = username

刪除Session

def logout(request):
    request.session.flush() 
    #刪除當前的會話session數據並刪除會話的Cookie,這用於確保前面的會話數據不可以再次被用戶的瀏覽器訪問
    request.session.delete()        # 刪除當前會話的所有Session數據
    return redirect("/login/")
​
del request.session['login']

所有 鍵、值、鍵值對:

request.session.keys()      #dict_keys(['login', 'user', '_session_expiry'])
request.session.values()    #dict_values(['OK', 'alex', 1209600])
request.session.items()     #dict_items([('login', 'OK'), ('user', 'alex'), ('_session_expiry', 1209600)])
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

其他session的屬性和方法:

# 會話session的key
request.session.session_key                 #k69mo6fu6qmvq7hpapdy54erpce1ksy5
# 將所有Session當前日期大於失效日期的數據刪除
request.session.clear_expired()
# 檢查會話session的key在資料庫中是否存在
request.session.exists("session_key")
# 設置會話Session和Cookie的超時時間
request.session.set_expiry(value)
    * 如果value是個整數,session會在數秒數後失效。
    * 如果value是個datatime或timedelta,session就會在這個時間後失效。
    * 如果value是0,用戶關閉瀏覽器session就會失效。
    * 如果value是None,session會依賴全局session失效策略。
request.session.set_expiry(60 * 60 * 24 * 14)
12.421 Django中的Session配置(settings.py)
1. 資料庫Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默認)
2. 快取Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的快取別名(默認記憶體快取,也可以是memcache),此處別名依賴快取的設置
3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 快取文件路徑,如果為None,則使用tempfile模組獲取一個臨時地址tempfile.gettempdir() 
4. 快取+資料庫
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
​
其他公用設置項:
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字元串(默認)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路徑(默認)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默認)
SESSION_COOKIE_SECURE = False                            # 是否Https傳輸cookie(默認)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支援http傳輸(默認)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默認)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否關閉瀏覽器使得Session過期(默認)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次請求都保存Session,默認修改之後才保存(默認)

12.5 Django中間件

中間件:在視圖函數執行之前和執行之後都可以做一些額外的操作,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。

process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)

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

自定義一個中間件示例:

from django.utils.deprecation import MiddlewareMixin
​
class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1裡面的 process_request")
    def process_response(self, request, response):
        print("MD1裡面的 process_response")
        return response

12.51 中間件版登錄驗證

中間件版的登錄驗證需要依靠session,所以資料庫中要有django_session表

middlewares.py:

class AuthMD(MiddlewareMixin):
    white_list = ['/login/', ]  # 白名單
    balck_list = ['/black/', ]  # 黑名單
def process_request(self, request):
        from django.shortcuts import redirect, HttpResponse
​
        next_url = request.path_info
        #print(request.path_info, request.get_full_path())
        if next_url in self.white_list or request.session.get("user"):
            return
        elif next_url in self.balck_list:
            return HttpResponse('This is an illegal URL')
        else:
            return redirect("/login/?next={}".format(next_url))

在settings.py中註冊:

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',
    'middlewares.AuthMD',
]

urls.py:

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

views.py:

from django.shortcuts import render, HttpResponse, redirect
def index(request):
    return HttpResponse('this is index')
​
def home(request):
    return HttpResponse('this is home')
​
def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
​
        if user == "Q1mi" and pwd == "123456":           
            request.session["user"] = user# 設置session           
            next_url = request.GET.get("next") # 獲取跳到登陸頁面之前的URL         
            if next_url:
                return redirect(next_url)# 如果有,就跳轉回登陸之前的URL         
            else:
                return redirect("/index/") # 否則默認跳轉到index頁面
    return render(request, "login.html")

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登錄頁面</title>
</head>
<body>
<form action="{% url 'login' %}">
    <p>
        <label for="user">用戶名:</label>
        <input type="text" name="user" id="user">
    </p>
    <p>
        <label for="pwd">密 碼:</label>
        <input type="text" name="pwd" id="pwd">
    </p>
    <input type="submit" value="登錄">
</form>
</body>
</html>

12.6 AJAX

AJAX(Asynchronous Javascript And XML)翻譯成中文就是「非同步的Javascript和XML」。即使用Javascript語言與伺服器進行非同步交互,傳輸的數據為XML(當然,傳輸的數據不只是XML)。AJAX 不是新的程式語言,而是一種使用現有標準的新方法。

AJAX 最大的優點是在不重新載入整個頁面的情況下,可以與伺服器交換數據並更新部分網頁內容。(這一特點給用戶的感受是在不知不覺中完成請求和響應過程)

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

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

  • 非同步交互:客戶端發出一個請求後,無需等待伺服器響應結束,就可以發出第二個請求。

AJAX優點:

  • AJAX使用JavaScript技術向伺服器發送非同步請求;

  • AJAX請求無須刷新整個頁面;

  • 因為伺服器響應內容不再是整個頁面,而是頁面中的部分內容,所以AJAX性能高;

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

urls.py:

urlpatterns = [
    ...
    url(r'^ajax_add/', views.ajax_add),
    url(r'^ajax_demo1/', views.ajax_demo1),
    ...   
]

views.py:

def ajax_demo1(request):
    return render(request, "ajax_demo1.html")
​
def ajax_add(request):
    i1 = int(request.GET.get("i1"))
    i2 = int(request.GET.get("i2"))
    ret = i1 + i2
    return JsonResponse(ret, safe=False)

ajax_demo1.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>AJAX局部刷新實例</title>
</head>
<body>
<input type="text" id="i1">+
<input type="text" id="i2">=
<input type="text" id="i3">
<input type="button" value="AJAX提交" id="b1">
<script src="/static/jquery-3.2.1.min.js"></script>
<script>
  $("#b1").on("click", function () {
    $.ajax({
      url:"/ajax_add/",                           #往哪裡發送請求
      type:"GET",                                 #請求的方法
      data:{"i1":$("#i1").val(),"i2":$("#i2").val()},#發送到後端的請求數據
      success:function (data) {                    #請求被正常響應時自動執行的回調函數
        $("#i3").val(data);
      }
    })
  })
</script>
</body>
</html>

12.61AJAX請求設置csrf_token

方式一:通過獲取隱藏的input標籤中的csrfmiddlewaretoken值,放置在data中發送

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>AJAX局部刷新實例</title>
</head>
<body>
{% csrf_token %}
<input type="text" id="i1">+
<input type="text" id="i2">=
<input type="text" id="i3">
<input type="button" value="AJAX提交" id="b1">
<script src="/static/jquery-3.2.1.min.js"></script>
<script>
  $("#b1").on("click", function () {
    $.ajax({
      url: "/cookie_ajax/",
      type: "POST",
      data: {"i1":$("#i1").val(),"i2":$("#i2").val(),"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
      },
      success: function (data) {
        console.log(data);
      }
    })
  })      
</script>
</body>
</html>

方式二:通過獲取返回的cookie中的字元串,放置在請求頭中發送。注意:需要引入一個jquery.cookie.js插件。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>AJAX局部刷新實例</title>
</head>
<body>
<input type="text" id="i1">+
<input type="text" id="i2">=
<input type="text" id="i3">
<input type="button" value="AJAX提交" id="b1">
<script src="/static/jquery-3.2.1.min.js"></script>
<script>
$("#b1").on("click", function () {
    $.ajax({
      url: "/cookie_ajax/",
      type: "POST",
      headers: {"X-CSRFToken": $.cookie('csrftoken')},  # 從Cookie取csrftoken,並設置到請求頭中
      data: {"i1":$("#i1").val(),"i2":$("#i2").val()},
      success: function (data) {
        console.log(data);
      }
    })
})
</script>
</body>
</html>

或者自己寫一個getCookie方法:

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            # Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

12.62 Django序列化

from app01 import models
def persons(request):
    ret = models.Person.objects.all()
    #re=models.Person.objects.all().values_list('data')
    # import json
    # person_list = []
    # for i in ret:
    # person_list.append({"name": i.name, "age": i.age})
    # s = json.dumps(person_list)
    from django.core import serializers
    s = serializers.serialize("json", ret) #序列化對象,取出對象所有屬性組成字典並序列化成json字元串
    #s = serializers.serialize("json", ret) 序列化日期類型
    return HttpResponse(s)

12.63 AJAX上傳文件

formData的基本用法:FormData對象,可以把所有表單元素的name與value組成一個queryString,提交到後台。只需要把 form 表單作為參數傳入 FormData 構造函數即可

利用 FormData 來上傳文件:

#上傳文件示例
<body>
<div>
    <input accept="image/*" type="file" name="avatar" id="f1">
    <input type="button" value="提交" id="b1">
</div>
<script src="/static/jquery-3.3.1.min.js"></script>
<script>
$("#b1").click(function () {
  var formData = new FormData();            #生成一個FormData對象
  formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
  formData.append("f1", $("#f1")[0].files[0]);#得到用戶選中的文件對象,並向formData對象中添加鍵值對數據
  $.ajax({
    url: "/upload/",
    type: "POST",
    processData: false,                     #告訴jQuery不要去處理髮送的數據
    contentType: false,                     #告訴jQuery不要去設置Content-Type請求頭
    data: formData,
    success:function (data) {                #請求被正常響應時自動執行的回調函數
      console.log(data)
    }
  })
})
</script>
</body>

或者:

var form = document.getElementById("form1");
var fd = new FormData(form);

這樣也可以直接通過ajax 的 send() 方法將 fd 發送到後台。

注意:由於 FormData 是 XMLHttpRequest Level 2 新增的介面,現在 低於IE10 的IE瀏覽器不支援 FormData。

views.py:

from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
​
def upload(request):
    if request.method == "POST":
        file_obj = request.FILES.get("avatar") # 獲取文件對象
        with open(file_obj.name, "wb") as f:   # file_obj.name  --> 上傳文件的文件名          
            for chunk in file_obj.chunks():    # 從該文件對象里一點一點讀取數據,寫入剛新建的文件句柄f
                f.write(chunk)
        # return JsonResponse("OK",safe=False)
        return HttpResponse("OK")
    return render(request, "upload.html")