之前有在用Django寫一些小網站,現在暑假想說再來複習一下之前買的這本書
於是我就把它寫成一系列的文章,也方便查語法
而且因為這本書大概是2014年出的,如今Django也已經出到2.多版
有些內容也變得不再支援或適用,而且語法或許也改變了
所以我會以最新版的Python和Django來修正這本書的內容跟程式碼
單純的http協定無法讓網站保持著某些資訊跟狀態,因為它就只是非常制式地接收要求,回應要求
每一次的要求,每一次的回應都是獨立的,因為http無法記錄任何狀態,它並不能保持著上一次要求所帶來的任何有用資訊
當然,透過GET與POST我們依然可以將資料透過新的request帶到下一個頁面,但是,這樣相當麻煩,而且沒有效率
將Http協定的不足分為以下三點:
伺服器不知道有某用戶
使用資料庫紀錄用戶的資訊便可解決
即使伺服器記錄了用戶的資訊,但不做比對,也是認不出用戶的
這時就需要仰賴登入及辨識了
只要用戶每次都辛苦地提供資訊,伺服器就可以辨識的出用戶
但我們不希望在同一個網站中每次要求一個新頁面時,都要重新登入一次
倒不如讓伺服器給用戶一塊餅乾,只要你拿這塊餅乾出來,我就可以認得你
這塊餅乾就是大家耳熟能詳的cookie了
cookie是伺服器儲存在瀏覽器的一小段訊息,它用來記住一些暫時性資訊並且能讓使用者跨頁面使用,每一次使用者透過瀏覽器向伺服器提出要求時,都會雙手奉上伺服器在稍早存在客戶端(瀏覽器)的cookie
透過這些cookies,伺服器便能掌握使用者的狀態,直到cookie失效那天
一個cookie其實就只是一個鍵值對,包含了cookie的名稱和cookie的值
在django中操作cookie就類似於使用字典那樣
def set_c(request):
response = HttpResponse('Set your lucky_number as 8')
response.set_cookie('lucky_number',8)
return response
set_cookie
第一個參數指定cookie的鍵,第二個參數指定cookie的值
def get_c(request):
if 'lucky_number' in request.COOKIES:
return HttpResponse('Your lucky_number is {0}'.format(request.COOKIES['lucky_number']))
else:
return HttpResponse('No cookies.')
cookies是儲存在瀏覽器端的,而用戶可以關閉cookies的功能,這會導致許多行為無效化
Http協定是明文協定,cookies在傳輸過程中容易被攔截、竄改、偽造等,並不安全
為了解決這些問題,引進了Session的機制
session之所以能夠解決這些問題是因為它是把資訊儲存在伺服端
那為何不直接將資料存在資料庫中?因為這只是暫時性的資訊,實在是沒有必要
不過django的session還是有用到資料庫的,所以在使用時要安裝app,也要同步資料庫
session跨頁面生存的方式:
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions', # <-確認有安裝
'django.contrib.messages',
'django.contrib.staticfiles',
'restaurants',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', # <-確認有安裝
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
如果第一次使用session記得要同步一下資料庫才能啟用session
python manage.py migrate
def use_session(request):
request.session['lucky_number'] = 8 # 設置lucky_number
if 'lucky_number' in request.session:
lucky_number = request.session['lucky_number'] # 讀取lucky_number
response = HttpResponse('Your lucky_number is ' + str(lucky_number))
return response
使用session時的規則:
1.使用字串作為session的鍵值
2.不要任意以底線作為session鍵值字串的開頭
3.不要對session及其屬性賦值
如果要刪除session
del request.session['lucky_number']
即可
打開shell
python manage.py shell
>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.all()[0]
>>> s.expire_date
datetime.datetime(2019, 7, 31, 10, 20, 57, 569595, tzinfo=<UTC>)
expire_date
是該session的有效期限,當超過這個時間時,session即失效
>>> s.session_data
'ODdhMzQxMWY0MzU0Nzg5YjgwYmRkZTZlZjJmYTBmZjBiMmQ4MzJkNzp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiJmMTQwNzM4OThkNzRlZjVjOWE1NzFiZmZhZDJhYjI1ZjYxNDgzNGQyIn0='
>>> s.get_decoded()
{'_auth_user_id': '1', '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend', '_auth_user_hash': 'f14073898d74ef5c9a571bffad2ab25f614834d2'}
session_data
是經過編碼的,必須利用get_decoded()
方法來取得編碼後的資料,而這會是一個字典
session ID 是透過cookie來保存的,這個cookie就叫session cookie
我們可以透過sessionid這個cookie名稱來取得cookie,而他的值就是編碼後的session ID
撰寫視圖函式
from django.contrib.sessions.models import Session
def session_test(request):
sid = request.COOKIES['sessionid']
s = Session.objects.get(pk=sid)
s_info = 'Session ID:' + sid + '<br>Expire_date:' + str(s.expire_date) +
'<br>Data:' + str(s.get_decoded())
return HttpResponse(s_info)
利用request.COOKIES來取得sessionid這個cookie的值,也就是session ID,接著我們利用模型的get方法去找到在資料庫中符合該ID的sessions(Session資料表的主鍵pk即為session id),最後我們把expire_date和session的內容都印出來
在頁面上按右鍵->檢查,就可以查看cookie及session資訊
另一個方法
def session_test(request):
sid = request.COOKIES['sessionid']
sid2 = request.session.session_key
s = Session.objects.get(pk=sid)
s_info = 'Session ID:' + sid + '<br>SessionID2:' + sid2 + '<br>Expire_date:' + str(s.expire_date) + '<br>Data:' + str(s.get_decoded())
return HttpResponse(s_info)
結果兩個方式拿到的session ID也是一樣的
這種預設以cookie記錄session ID來達成跨頁面的方式雖然方便,但讀者們還是要確認使用者端的cookie功能是否被開啟,透過以下步驟來測試:
1.利用HttpRequest.session.set_test_cookie()
設置測試cookie
2.利用HttpRequest.session.test_cookie_worked()
來檢查cookie是否被允許使用
3.利用HttpRequest.session.delete_test_cookie()
來刪除測試cookie
如果cookie未被開啟,那我們只得利用URL查詢的方式,去傳遞session ID,不過這會相當麻煩
可以在settings.py中設定參數
參數 | 意義 | 預設值
------------- | -------------
SESSION_EXPIRE_AT_BROWSER_CLOSE | 決定session是否在瀏覽器關閉時結束 | False
SESSION_COOKIE_AGE | session(cookie)的有效時間 | 1209,600秒,兩週
views.py
...
def list_restaurants(request):
restaurants = Restaurant.objects.all()
request.session['restaurants'] = restaurants # 試著利用session保存模型物件
return render_to_response('restaurants_list.html', locals())
...
結果出現錯誤
在settings.py中加入
SESSION_SERIALIZER = 'django.contrib.sessions.serializers' + '.PickleSerializer'
就能解決了
pickle是python儲存物件的一種特殊機制,可以將物件訊息化變成一連串的編碼,方便儲存或紀錄,而從這些編碼要回復為物件時也是需要經由pickle才能回復,這個特殊的機制有個名詞:持久化,這裡我們要保存的是模型物件,無法用json格式轉化儲存,所以改為PickleSerializer
不知道是不是我個人的問題
我必須在setting加入SESSION_ENGINE的設定
才能完成12.3.6 用Session儲存模型物件的部分,否則會顯示incorrect padding error。
感謝作者的文章教學!