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',
]
posted @ 2020-07-07 16:38  给你加马桶唱疏通  阅读(141)  评论(0编辑  收藏  举报