之前有在用Django写一些小网站,现在暑假想说再来複习一下之前买的这本书
于是我就把它写成一系列的文章,也方便查语法
而且因为这本书大概是2014年出的,如今Django也已经出到2.多版
有些内容也变得不再支援或适用,而且语法或许也改变了
所以我会以最新版的Python和Django来修正这本书的内容跟程式码
目录:django系列文章-Django学习纪录
12. Cookies与Sessions
12.1 Http协定的不足
单纯的http协定无法让网站保持着某些资讯跟状态,因为它就只是非常制式地接收要求,回应要求
每一次的要求,每一次的回应都是独立的,因为http无法记录任何状态,它并不能保持着上一次要求所带来的任何有用资讯
当然,透过GET与POST我们依然可以将资料透过新的request带到下一个页面,但是,这样相当麻烦,而且没有效率
将Http协定的不足分为以下三点:
12.1.1 根本不知道使用者
伺服器不知道有某用户
使用资料库纪录用户的资讯便可解决
12.1.2 知道使用者但不会做资料比对
即使伺服器记录了用户的资讯,但不做比对,也是认不出用户的
这时就需要仰赖登入及辨识了
12.1.3 知道使用者,也会比对,但使用者在每一次的请求中都要比对
只要用户每次都辛苦地提供资讯,伺服器就可以辨识的出用户
但我们不希望在同一个网站中每次要求一个新页面时,都要重新登入一次
倒不如让伺服器给用户一块饼乾,只要你拿这块饼乾出来,我就可以认得你
12.2 Cookies-好饼乾,不吃吗?
这块饼乾就是大家耳熟能详的cookie了
cookie是伺服器储存在浏览器的一小段讯息,它用来记住一些暂时性资讯并且能让使用者跨页面使用,每一次使用者透过浏览器向伺服器提出要求时,都会双手奉上伺服器在稍早存在客户端(浏览器)的cookie
透过这些cookies,伺服器便能掌握使用者的状态,直到cookie失效那天
一个cookie其实就只是一个键值对,包含了cookie的名称和cookie的值
在django中操作cookie就类似于使用字典那样
12.2.1 设置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的值
12.2 读取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.')
12.2.3 饼乾的问题
cookies是储存在浏览器端的,而用户可以关闭cookies的功能,这会导致许多行为无效化
Http协定是明文协定,cookies在传输过程中容易被拦截、窜改、伪造等,并不安全
为了解决这些问题,引进了Session的机制
12.3 Session
session之所以能够解决这些问题是因为它是把资讯储存在伺服端
那为何不直接将资料存在资料库中?因为这只是暂时性的资讯,实在是没有必要
不过django的session还是有用到资料库的,所以在使用时要安装app,也要同步资料库
session跨页面生存的方式:
我们先使用预设的第一种
12.3.1 安装Session App
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
12.3.2 使用Session
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']
即可
12.3.3 Session资料库
打开shell
python manage.py shell
>>> from django.contrib.sessions.models import Session>>> s = Session.objects.all()[0]>>> s.expire_datedatetime.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()
方法来取得编码后的资料,而这会是一个字典
12.3.4 Session cookie
session ID 是透过cookie来保存的,这个cookie就叫session cookie
我们可以透过sessionid这个cookie名称来取得cookie,而他的值就是编码后的session ID
撰写视图函式
from django.contrib.sessions.models import Sessiondef 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,不过这会相当麻烦
12.3.5 Session的持续性
可以在settings.py中设定参数
参数 | 意义 | 预设值
------------- | -------------
SESSION_EXPIRE_AT_BROWSER_CLOSE | 决定session是否在浏览器关闭时结束 | False
SESSION_COOKIE_AGE | session(cookie)的有效时间 | 1209,600秒,两週
12.3.6 用Session储存模型物件
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