session源碼剖析

session機制採用的是一種在客戶端與服務端之間保持狀態的解決方案,因爲採用服務器端保持狀態的方案在客戶端也要保存標識,session機制也要藉助於cookie機制達到目的。session保存了客戶的登陸信息,可是不須要把用戶的全部信息都保存在session中,咱們只須要讓與用戶數據關聯的信息保存在session中就能夠了。數據庫

request.session[「user_id」] = 2  # 設置session  關聯id比關聯username好,由於username可能不惟一
request.session.get["user_id"]  # 取session

接下來看源碼:django

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文件裏面找。服務器

另一點值得注意的是:cookie

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

就接下來看:session

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必定是它裏面的變量名

繼續去看源碼:dom

from django.contrib.sessions.backends import db  # 點擊db看源碼

能夠看到,SessionStore是一個類的名字,self.SessionStore這裏就是一個類名,全部的功能都封裝在SessionStore裏面,很是重要。函數

class SessionStore(SessionBase):...

走到這裏,__init__這個函數就執行完了。繼續執行process_request方法。spa

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,就會看到:code

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 # 返回響應

 (完)

相關文章
相關標籤/搜索