django手撸系列-实现存文件的session功能
一. 需求
手动实现一个存文件的session
二. 实现
第一步: 应用下或者项目下创建文件夹middleware
第二步: 在middleware文件夹下创建token_middle.py文件
第三步: 书写代码
import os
import json
import uuid
from django.utils.deprecation import MiddlewareMixin
from day80_django import settings
class NotStrError(BaseException):
def __init__(self, value='Not a string type!'):
self.value = value
super().__init__()
def __str__(self):
return self.value
class CustomSessionStore(dict):
def __init__(self, session_key=None):
super().__init__()
if not os.path.isdir(settings.CUSTOM_SESSION_PATH):
os.mkdir(settings.CUSTOM_SESSION_PATH)
self.file_path = os.path.join(settings.CUSTOM_SESSION_PATH, settings.CUSTOM_SESSION_FILE)
self.read_session_data(session_key)
self.session_key = session_key
self.modify = False
self.is_flush = False
# print('file_path:', self.file_path)
# print('self.session_data:', self.session_data)
def read_session_data(self, session_key):
"""
来的时候从文件中获取
区分2种可能的情况:
第一种情况: None 没有登录时
第二种情况: session_key 登陆时随机字符串
如果随机字符串, 那么读取文件找到对应的session_data,
将用户绑定的session的key封装成字典的key,
将用户保存的session_data封装成字典的value
那么读取文件找到对应的session_data.
如果没有对于关系返回空字典
取的时候: request.session.get()
"""
if session_key is None:
return
if os.path.isfile(self.file_path):
with open(self.file_path, 'r', encoding='utf-8') as f:
for line in f:
res = line.strip()
if not res:
continue
# session_dict: {session_key: {key: value}}
session_dict = json.loads(res)
# print('session_dict:', session_dict)
if session_key in session_dict:
# session_data: {key: value}
# session_data -> request.session[key] = value value就是用户的自定义的key value就是用户存入的数据
session_data = session_dict.get(session_key)
# print('session_data:', session_data)
for key, value in session_data.items():
setattr(self, key, value)
break
def write_session_data(self):
"""走的时候保存到文件中"""
# print('self.session_dict', self.session_dict)
with open(self.file_path, 'a', encoding='utf-8') as f:
f.write(f'{json.dumps(self.session_dict)}\n')
def get(self, item):
"""自定义request.session.get"""
if isinstance(item, str):
raise NotStrError()
return self.__dict__.get(item)
def flush(self):
"""自定义request.session.flush"""
temp_file = os.path.join(settings.CUSTOM_SESSION_PATH, 'temp.txt')
with open(self.file_path, 'r', encoding='utf-8') as f1, open(temp_file, 'w', encoding='utf-8') as f2:
for line in f1:
res = line.strip()
if not res:
continue
session_dict = json.dumps(res)
if self.session_key not in session_dict:
f2.write(line)
os.remove(self.file_path)
os.rename(temp_file, self.file_path)
self.file_path = temp_file
self.is_flush = True
def __setitem__(self, key, value):
# self.session_dict = {str(self.session_key): {key: value}}
self.session_key = uuid.uuid1()
self.session_dict = {str(self.session_key): {key: value}}
self.modify = True
# print('self.session_dict:', self.session_dict)
def __str__(self):
return f'{self.__dict__}'
class CustomSessionMiddleware(MiddlewareMixin, CustomSessionStore):
def process_request(self, request):
"""
1. 请求来的时候先从COOKIES中获取session_key
第一种情况: 浏览器没有登录过, 没有session_key. 获取的返回值为None
第二种情况: 浏览器已经登录过, 含有session_key. 获取的返回值就是session_key
2. 当用户设置session时(如: request.custom_session['key']=value), 会触发某个对象类中的__setitem__属性.
因此我们对request赋值的custom_session属性就应该是一个对象. 那么我们需要设计一个类. 这个类的功能就是存储及读取功能.
存储: 存储到文件中.
格式: {session_key: {key: value}} -> json格式 文件中的每一行就应该是对应的一条用户session数据
3. 我们需要将这两种情况的session_key在CustomSessionStore中进行区分处理
有: 那么返回的 request.custom_session 对象中视图中就通过 request.custom_session.get(key) 可以取出 用户保存的value
无: 那么返回的 request.custom_session 对象中视图中就通过 request.custom_session.get(key) 取出的应该是None
.get(key)这是一个字典的操作, 那么CustomSessionStore类需要继承字典. (注意: 需要自定义get方法, 字典的get方法不知道为什么不能使用?)
3. CustomSessionStore(session_key)返回的对象赋值给了 request.custom_session (提示: 这是一个字典对象, 因为继承了字典)
当操作这个对象时. 视图中就可以通过.get()方法通过key获取value了 -> request.custom_session.get('key')
返回的结果又2种:
第一种: 用户未认证之前 返回 None
第二种: 用户认证之后 返回 value
4. request.custom_session = CustomSessionStore(session_key)这一步视图中就可以对自定义的session进行操作了
强调: request.custom_session是一个对象
"""
session_key = request.COOKIES.get(settings.CUSTOM_SESSION_COOKIE_NAME)
# print("session_key:", session_key)
request.custom_session = CustomSessionStore(session_key)
# print('request.custom_session:', request.custom_session)
# print('request.custom_session.__dict__:', request.custom_session.__dict__, type(request.custom_session))
def process_response(self, request, response):
"""
views.py中实现:
当指定request.custom_session['key']=value这种设值操作以后, 应该自动触发该对象类中的定义的__setitem__方法
为对象绑定先生成的session_key, 再为对象绑定视图对应的key:value数据
封装成一个字典. 作为custom_session_id的value. -> {custom_session_id, {key: value}}
设置功能:
1. 响应走的时候. 应该判断用户是否对 request.custom_session['key']=value 进行了操作. 如果操作了. 同时那么也应该
在自动触发的__setitem__方法中对self.modify属性进行修改, 将初始化中默认的False, 修改成True.
2. 当self.modify为True时那么该缩进下的子代码块应该写将之前自动触发生成的 {custom_session_id, {key: value}} 以json格式
方式存入文件当中. 那么就应该通过请求来时绑定好的对象 request.custom_session 中进行绑定保存数据的操作.
保存格式: json格式
保存的数据: self.session_dict -> {custom_session_id, {key: value}}
3. 在服务端将用户的session_dict进行保存以后, 接下来就是将session_id通过obj.set_cookie写入浏览器
key就是settions中实现写好的key名
value就是视图中执行 request.custom_session['key']=value 操作以后生成的session_key
"""
obj = response
# 注销
print('request.custom_session.is_flush:', request.custom_session.is_flush)
if request.custom_session.is_flush:
obj.delete_cookie(settings.CUSTOM_SESSION_COOKIE_NAME)
self.is_flush = False
# 设置
if self.modify:
request.custom_session.write_session_data()
obj.set_cookie(settings.CUSTOM_SESSION_COOKIE_NAME, request.custom_session.session_key)
# print('request.custom_session.session_key:', request.custom_session.session_key)
# print('request.custom_session.session_key:', request.custom_session.session_dict)
self.modify = False
return obj
第四步: 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',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# 自定义中间件
'app01.custom_middleware.definition_session.CustomSessionMiddleware',
]