session机制采用的是一种在客户端与服务端之间保持状态的解决方案,由于采用服务器端保持状态的方案在客户端也要保存标识,session机制也要借助于cookie机制达到目的。session保存了客户的登录信息,但是不需要把用户的所有信息都保存在session中,我们只需要让与用户数据关联的信息保存在session中就可以了。

request.session[“user_id”] = 2  # 设置session  关联id比关联username好,因为username可能不唯一
request.session.get["user_id"]  # 取session

接下来看源码:

from django.contrib.sessions.middleware import SessionMiddleware  # 点击SessionMiddleware看源码
import time
from importlib import import_module from django.conf import settings
from django.contrib.sessions.backends.base import UpdateError
from django.core.exceptions import SuspiciousOperation
from django.utils.cache import patch_vary_headers
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import cookie_date class SessionMiddleware(MiddlewareMixin):
  # 下面两个方法是中间件走的
def __init__(self, get_response=None):
self.get_response = get_response # 这是个空变量,get_response=None
engine = import_module(settings.SESSION_ENGINE) # 用了两个settings
self.SessionStore = engine.SessionStore def process_request(self, request):...
# 下面一个方法是views视图要走的
def process_response(self, request, response):...

先看这个settings,点击

from django.conf import global_settings  # 这里面有global_settings,点击看源码

......

settings = LazySettings()
SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 这个文件里面会看到SESSION_ENGINE

这里面可与看到SESSION_ENGINE是一条配置信息,这里默认将session的信息放到djano数据库里面的session表里面。所以我们可以通过这里面的配置,将session信息放到缓存、文件、Memcache中。另外可以从from django.conf import settings看出,我们这里引用的settings不仅仅是我们django的settings.py文件还有global_settings.py文件,一共两套,先找我们常用的settings.py如果找不到,再去global_settings.py文件里面找。

另外一点值得注意的是:

engine = import_module(settings.SESSION_ENGINE)  
# SESSION_ENGINE = 'django.contrib.sessions.backends.db' 是一个字符串
# import_module("django.contrib.sessions.backends.db") 和 importdjango.contrib.sessions.backends.db 效果是一样的
# 引入模块之后,再赋值给 engine,所以优先用自己写的engine

就接下来看:

class SessionMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response # 这是个空变量,get_response=None
engine = import_module(settings.SESSION_ENGINE) # 用了两个settings
self.SessionStore = engine.SessionStore # 这里可以看到,因为模块名是engine,所以SessionStore一定是它里面的变量名

继续去看源码:

from django.contrib.sessions.backends import db  # 点击db看源码

可以看到,SessionStore是一个类的名字,self.SessionStore这里就是一个类名,所有的功能都封装在SessionStore里面,非常重要。

class SessionStore(SessionBase):...

走到这里,__init__这个函数就执行完了。继续执行process_request方法。

def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)

先去常用的settings.py文件里找,这里找不到,再去global_settings.py里面找SESSION_COOKIE_NAME,就会看到:

SESSION_COOKIE_NAME = 'sessionid'  # 这里拿到了sessionid

session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)# 拿到sessin后进行了一个取的操作,没有sessionid键值对,session_key就是None

所以用户登陆进来,一定会去取这个cookie,无论是第一次还是第二次登陆,不管cookie是不是空的,都要去取cookie里面的sessionid这个键,如果是第一次登陆肯定没有sessionid这个键,如果是第二次第三次登陆肯定有我给你的这个键。

request.session = self.SessionStore(session_key)
# SessionStore是一个功能类,里面传一个参数,就是实例化一个对象,所以request.session就是一个对象。

继续去db里面看SessionStore做了什么。

class SessionStore(SessionBase):

    def __init__(self, session_key=None):  # session_key默认为空
super(SessionStore, self).__init__(session_key)

将session_key赋予了__init__方法,继承了父类方法,如果还要执行父类的__init__方法就用super()方法。

进入SessionBase这个父类看__init__方法。

class SessionBase(object):

    TEST_COOKIE_NAME = 'testcookie'
TEST_COOKIE_VALUE = 'worked' __not_given = object() def __init__(self, session_key=None):
self._session_key = session_key # 私有化了
self.accessed = False # 一种状态, 控制,你能不能进入
self.modified = False # 一种状态,能不能修改
self.serializer = import_string(settings.SESSION_SERIALIZER) # 序列化,用到JSONSerializer进行序列化的,去global_settings找得到

到这一步request_session方法也走完了,其实就是得到一个request.session对象。

这样就通过中间件,就该去执行视图了。

__init__方法和process_request()方法属于走中间件的内容。

视图流程views:

request.session["user_id"] = 1 # 这是一个赋值操作

继续去SessionBase里面看源码:

class SessionBase(object):  # 里面还有下面三个方法

    def __getitem__(self, key):  # 这个是获取值操作,但是必须是print(a['k'])这种操作才能出发这个方法
return self._session[key] def __setitem__(self, key, value): # 这个是设置值操作,但是赋值操作必须是a['k'] = v 这种形式才会触发这个方法
self._session[key] = value
self.modified = True def __delitem__(self, key): # 这个是删除值操作
del self._session[key]
self.modified = True

然后看这个_session的功能。SessionBase里面的方法里面有_session,那么它还有类方法:

_session = property(_get_session)  # 会找到这个,后面加property就是去执行_get_session方法去了
    def _get_session(self, no_load=False):

        self.accessed = True  # 这个前面默认False,通过这个状态就可以区分登陆没有
try:
return self._session_cache # 返回的就是session的缓存,和db有关,就可以认为缓存里带着db里面的数据,这里就是数据真正保存的地方
except AttributeError:
if self.session_key is None or no_load:
self._session_cache = {} # 返回的是一个字典形式
else:
self._session_cache = self.load() # 这里是反序列化回来的时候
return self._session_cache # 把最终的这个值返回 _session = property(_get_session)

赋值操作:

request.session["user_id"] = 1 # 这个操作结束后,并没有存入数据库,而是在缓存中,因为没有执行save()操作。

继续走到相应的操作,但是还没返回到用户的客户端。

    def process_response(self, request, response):
try:
accessed = request.session.accessed # 先取出这两个变量,登陆成功执行了request.session["user_id'] = 1 他们都是True了
modified = request.session.modified # 如果没有执行这个赋值操作,他们依然是False
empty = request.session.is_empty() # 点击is_empty()看源码
    def is_empty(self):
try:
# not boo(self._session_key) 这里是True 因为这时第一次登陆_session_key时None
# not self._session_cache 是 False 因为这是缓存里有值
# True and False 结果就是 False 了,就是返回False
return not bool(self._session_key) and not self._session_cache
except AttributeError:
return True

所以empty就是None了

def process_response(self, request, response):
try:
        accessed = request.session.accessed  # accessed变量,登陆成功并执行了request.session['user_id'] = 1 状态就变成True
modified = request.session.modified # modified变量,登陆成功并自行了request.session['user_id'] = 1 状态就变成Trye
empty = request.session.is_empty()
except AttributeError:
pass
else: # 下面就是检验就没有进行request.session["user_id"] = 2 这个赋值操作
if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: # empty是False,所以这句话就不执行了
response.delete_cookie(
settings.SESSION_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN,
)
else: # 就走这句话
if accessed: # 刚才accessed是True 所以这句话执行
patch_vary_headers(response, ('Cookie',))
# not empty 是True
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
# 从这 和过期时间相关
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
# 到这结束 和过期时间相关
if response.status_code != 500: # 继续 如果没有响应错误,就执行下面的代码
try:
request.session.save() # 这里就是把数据保存到数据库了
except UpdateError:
raise SuspiciousOperation(
"The request's session was deleted before the "
"request completed. The user may have logged "
"out in a concurrent request, for example."
)
response.set_cookie(
# 这里是session_id 的key
settings.SESSION_COOKIE_NAME,
# 这里是session_id的value,这里就拿到了随机字符串
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
)
return response # 返回响应

(完)

session源码剖析的更多相关文章

  1. django session源码剖析

    首先要明白,session和cookie,session是保存在服务器端,cookie存储在浏览器上,我们称为客户端,客户端向服务端发送请求时,会将cookie一起发送给服务端.服务端接收到请求后,会 ...

  2. SpringMVC源码剖析(二)- DispatcherServlet的前世今生

    上一篇文章<SpringMVC源码剖析(一)- 从抽象和接口说起>中,我介绍了一次典型的SpringMVC请求处理过程中,相继粉墨登场的各种核心类和接口.我刻意忽略了源码中的处理细节,只列 ...

  3. Appuim源码剖析(Bootstrap)

    Appuim源码剖析(Bootstrap) SkySeraph Jan. 26th 2017 Email:skyseraph00@163.com 更多精彩请直接访问SkySeraph个人站点:www. ...

  4. ThreadLocal终极源码剖析

    目录一.ThreadLocal1.1 源码注释1.2 源码剖析      散列算法-魔数0x61c88647      set操作    get操作    remove操作1.3 功能测试1.4 应用 ...

  5. 豌豆夹Redis解决方案Codis源码剖析:Proxy代理

    豌豆夹Redis解决方案Codis源码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描述: Codis is a proxy base ...

  6. ThreadLocal终极源码剖析-一篇足矣!

    本文较深入的分析了ThreadLocal和InheritableThreadLocal,从4个方向去分析:源码注释.源码剖析.功能测试.应用场景. 一.ThreadLocal 我们使用ThreadLo ...

  7. Flask核心机制--上下文源码剖析

    一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很 ...

  8. Django----djagorest-framwork源码剖析

    restful(表者征状态转移,面向资源编程)------------------------------------------->约定 从资源的角度审视整个网络,将分布在网络中某个节点的资源 ...

  9. Rest_Framework之认证、权限、频率组件源码剖析

    一:使用RestFramwork,定义一个视图 from rest_framework.viewsets import ModelViewSet class BookView(ModelViewSet ...

随机推荐

  1. Mac os x下几款mysql客户端

    Mac os x几款mysql客户端 1,Navicat 这是一款可支持多种数据库的客户端,可支持mysql,sqlite,oracle,sql server等数据库.当然也可以选择只支持某种数据库的 ...

  2. linux常见故障处理

    目录 一. 文件和目录类 1.1 File exist 文件已经存在 1.2 No such file or directory 没有这个文件或目录(这个东西不存在) 1.3 command not ...

  3. gitignore规则探究

    PS:转自https://blog.csdn.net/o07sai/article/details/81043474 网上有好多gitignore的帖子,文章,都说很简单的.但是我怎么就用不好呢? 我 ...

  4. 网络流24题——魔术球问题 luogu 2765

    题目描述:这里 这道题是网络流问题中第一个难点,也是一个很重要的问题 如果直接建图感觉无从下手,因为如果不知道放几个球我就无法得知该如何建图(这是很显然的,比如我知道 $1+48=49=7^2$ ,可 ...

  5. php 常用的知识点归集(上)

    1.php if...else 在html中的替代语法 <?php $name = 'ok' ?> <?php if ($name == '1ok') : ?> <div ...

  6. 解决idea server 控制台乱码问题

    如果网上其他方式不奏效,可尝试下面方法: 找到tomcat 的配置文件:"D:\Program Files\apache-tomcat-9.0.17\conf\logging.propert ...

  7. percona-toolkit安装

    https://www.percona.com/downloads/percona-toolkit/LATEST/ #下载wget https://www.percona.com/downloads/ ...

  8. [转] Vue中异步错误处理

    一般在一个项目开始之前,我们一般会对现有的框架做一定功能上的丰富,比如对ajax请求功能的二次封装,封装的功能可能包含了:通用错误处理,请求过滤,响应过滤等等.如果我们封装的函数叫request,那么 ...

  9. ELK配置

    安装logstash docker pull logstash docker run -it --rm logstash -e 'input { stdin { } } output { stdout ...

  10. 嵌入 Office ,doc|docx|xls|xlsx|ppt|pptx|pdf|等

    <iframe src="https://view.officeapps.live.com/op/embed.aspx?src=http%3A%2F%2Fcdn%2Dresource% ...