1. 程式人生 > Django入門教學 >29 操作 Cookie 和 Session

29 操作 Cookie 和 Session

上一節介紹了 Cookie 和 Session 的相關概念,本節就要在 Django 中操作 Cookie 和 Session,同時我也會繼續帶領大家追蹤相關的程式碼,這樣可以更好的理解相關操作。

操作 Cookie 同樣是考察4個基本動作:增刪改查。現在分別從這4個角度看 Django 如何操作 Cookie :

:對於檢視函式或者檢視類的三種返回 Response 響應 (HttpResponse、render、redircet),之前的做法是直接 return,現在可以在 return 之前,使用 set_cookie() 或者 set_signed_cookied()

方法給客戶端頒發一個 cookie,然後再帶著頒發的 cookie 響應使用者請求。操作程式碼結構如下。

def xxxx(request, *args, **kwargs):
    # ...
    
    rep = HttpResponse(...)
    # 或者
    rep = render(request, ...)
    # 或者
    rep = redirect( ...)

    # 兩種設定cookie的方法,一種不加salt,另一個加salt 
    rep.set_cookie(key, value,...)
    rep.set_signed_cookie(
key, value, salt='加密鹽', max_age=None, ...) return rep

:查詢 cookie 是在傳送過來的 HTTP 請求中的,因此對應的查詢 Cookie 方法封裝在 HttpRequest 類中,對應的操作語句如下:

request.COOKIES['key']
request.COOKIES.get['key']
# 對應前面使用前面加密的cookie
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

:呼叫前面的 set_cookie()

或者 set_signed_cookie() 方法修改 Cookie 即可;

:直接使用 HttpReponse 類的 delete_cookie() 刪除 cookie 中對應 key 值。

案例1:Django 中 Cookie 實操。我們在前面的登入表單功能上改造檢視函式,保證一次登入後,後續再次 GET 請求時能自動識別登入使用者。此外還設定一個 Cookie 過期時間,過期之後再次 GET 請求時又回到登入頁面。

調整登入表單的檢視類:

class TestFormView2(TemplateView):
    template_name = 'test_form2.html'

    def get(self, request, *args, **kwargs):
        success = False
        form = LoginForm()
        print("[{}] cookies:{}".format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), request.COOKIES))
        if request.get_signed_cookie('user', default='anonymous', salt=default_salt) == 'spyinx':
            success = True
        return self.render_to_response(context={'success': success, 'form': form})

    def post(self, request, *args, **kwargs):
        form = LoginForm(request.POST)
        success = True
        err_msg = ""
        rep = self.render_to_response(context={'success': success, 'err_msg': err_msg, 'form': form})
        if form.is_valid():
            login_data = form.clean()
            name = login_data['name']
            password = login_data['password']
            if name != 'spyinx' or password != 'SPYinx123456':
                success = False
                err_msg = "使用者名稱密碼不正確"
            else:
                print('設定cookie')
                rep.set_signed_cookie('user', 'spyinx', salt=default_salt, max_age=10)
        else:
            success = False
            err_msg = form.errors['password'][0]
        return rep

可以看到,在 get()方法中我們通過 get_signed_cookie() 方法獲取 cookie 中的 user 資訊,判斷是否為 spyinx。若正確則返回成功登入的頁面,否則返回登入頁面。在 post() 方法中,對於登入成功的情況我們通過 set_signed_cookie() 方法頒發了一個 cookie 給客戶端,並設定過期時間為10s,後續客戶端的請求中都會自動帶上這個 cookie。

2. Django 中操作 Session

2.1 Session 的相關配置

由於 Session 的資料是儲存在伺服器端的,所以很多工作是需要在伺服器端來完成的,所以 Django 中 Session 的操作相比 Cookie 操作會略顯複雜。首先需要介紹 Django 中和 Session 相關的配置,同樣是在 settings.py 檔案中:

啟用 Session:需要在 MIDDLEWARE 值中新增相應的 Session 中介軟體,去對 Session 攔截和處理。另外,還需要再INSTALLED_APPS 中註冊 Session 應用。Django 中預設是有這個配置的。

MIDDLEWARE = [
    # ...
    'django.contrib.sessions.middleware.SessionMiddleware',
    # ...
]

INSTALLED_APPS = [
    # 啟用 sessions 應用
    'django.contrib.sessions',
]

配置 Session 引擎:主要是配置 Session 儲存方式,比如資料庫儲存、記憶體儲存、檔案系統儲存等。

# 資料庫Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'       # 預設引擎

# 快取Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  
# 使用的快取別名(預設記憶體快取,也可以是memcache),此處別名依賴快取的設定
SESSION_CACHE_ALIAS = 'default'                              

# 檔案Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    
# 快取檔案路徑,如果為None,則使用tempfile模組獲取一個臨時地址tempfile.gettempdir() 
SESSION_FILE_PATH = None                                     

# 快取+資料庫
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'       

# 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' 

其他配置如下:

# Session的cookie儲存在瀏覽器上時的key,即:sessionid=隨機字串(預設)
SESSION_COOKIE_NAME = "sessionid"                      
# Session的cookie儲存的路徑(預設)
SESSION_COOKIE_PATH = "/"        
# Session的cookie儲存的域名(預設)
SESSION_COOKIE_DOMAIN = None       
# 是否Https傳輸cookie(預設)
SESSION_COOKIE_SECURE = False       
# 是否Session的cookie只支援http傳輸(預設)
SESSION_COOKIE_HTTPONLY = True       
# Session的cookie失效日期(2周)(預設)
SESSION_COOKIE_AGE = 1209600   
# 是否關閉瀏覽器使得Session過期(預設)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False   
# 是否每次請求都儲存Session,預設修改之後才儲存(預設)
SESSION_SAVE_EVERY_REQUEST = False           

關於上述這些未出現在 settings.py 中的配置,預設的值都會在 django/conf/global_settings.py 中找到,如下圖所示:

圖片描述

2.2 操作 Session

在 Django 中,如果我們配置好了 Session 中介軟體並註冊了 Session應用 ,那麼任何檢視函式的第一個引數,也就是 HttpRequest 物件,將會包含一個 session 屬性,我們可以在檢視函式的任何位置使用 request.session 讀取和寫 Session 資訊。這個 session 屬性是前面配置的 SESSION_ENGINE 模組中 SessionStore 類的例項,而SessionStore 類又繼承自 django.contrib.sessions.backends.base.SessionBase 類,它的具有如下操作方法:

  • get(self, key, default=None):獲取 Session 中的 key 值對應的 value。這種方式比較推薦。因為在 key 不存在時可以取預設值,而request.session[key] 這樣的寫法在 key 不存在時會丟擲異常;
  • pop(key, default=__not_given):返回 Session 中 key 對應的 value 值,並將其從 Session 中刪除;
  • keys():返回 Session 中所有的 key 值
  • setdefault(key, value):給某個 key 設定對應的預設 value
  • set_expiry(value):給 Session 設定對應的過期時間;

還有很多的方法可以在原始碼中找到,我們會在第3部分詳細介紹這些程式碼。可以看到,目前我們操作 Session 就像操作字典那樣簡單,只需要使用 SessionBase 提供的各種方法即可隨意操作 Session,這些都是 Django 在背後給我們做了許多工作,我們也會在第3部分詳細介紹這些工作。

實驗2:使用 Session 完成一個簡單的登入操作,和上面 Cookie 實驗類似。我們的模板檔案不變,同樣只需要改造下檢視函式即可。

改造檢視函式,使用 Session 儲存登入資訊

class TestFormView2(TemplateView):
    template_name = 'test_form2.html'

    def get(self, request, *args, **kwargs):
        success = False
        form = LoginForm()
        if request.session.get('has_login', False):
            return HttpResponse('已經登陸成功,無需再次登陸')
        return self.render_to_response(context={'success': success, 'form': form})

    def post(self, request, *args, **kwargs):
        form = LoginForm(request.POST)
        success = True
        err_msg = ""
        if form.is_valid():
            login_data = form.clean()
            name = login_data['name']
            password = login_data['password']
            if name != 'spyinx' or password != 'SPYinx123456':
                success = False
                err_msg = "使用者名稱密碼不正確"
            else:
                print('設定session')
                request.session['has_login'] = True
                # 設定10s後過期
                request.session.set_expiry(10)
        else:
            success = False
            err_msg = form.errors['password'][0]
        return self.render_to_response(context={'success': success, 'err_msg': err_msg, 'form': form})

我們執行服務,來看相應的演示效果:

上面也可以看到,這裡實現了和之前 Cookie 一樣的效果。不過不同的是,在 Django 中我們預設的使用資料庫的方式儲存 Session 資料,這種方式在使用者量大時頻繁讀寫資料庫會拖累 Web 服務的響應時間。

從前面的操作 Cookie 講解中,我們只用到了和兩部分的方法,分別對應 HttpResponse 和 HttpRequest 兩個類。接下來,我們去對應的原始碼中查詢所涉及的和 Cookie 相關的程式碼。

request.COOKIES['xxx']
request.COOKIES.get('xxx', None)
# 原始碼位置:django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
    # ...
    
    @cached_property
    def COOKIES(self):
        raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
        return parse_cookie(raw_cookie)
    # ...
# 原始碼位置:django/http/cookie.py
from http import cookies

# For backwards compatibility in Django 2.1.
SimpleCookie = cookies.SimpleCookie

# Add support for the SameSite attribute (obsolete when PY37 is unsupported).
cookies.Morsel._reserved.setdefault('samesite', 'SameSite')


def parse_cookie(cookie):
    """
    Return a dictionary parsed from a `Cookie:` header string.
    """
    cookiedict = {}
    for chunk in cookie.split(';'):
        if '=' in chunk:
            key, val = chunk.split('=', 1)
        else:
            # Assume an empty name per
            # https://bugzilla.mozilla.org/show_bug.cgi?id=169091
            key, val = '', chunk
        key, val = key.strip(), val.strip()
        if key or val:
            # unquote using Python's algorithm.
            cookiedict[key] = cookies._unquote(val)
    return cookiedict

上面的程式碼並不複雜,在 WSGIRequest 類中的 COOKIES 屬性是先從客戶端請求中取出 Cookie 資訊,呼叫 get_str_from_wsgi() 方法是從 WSGI 中拿到對應的 Cookie 字串。接下來用 parse_cookie() 方法將原始 Cookie 字串中的 key=value 解析出來做成字典形式並返回。這就是為什麼我們能像操作字典一樣操作 request.COOKIES 的原因。下面的方法是實驗1中呼叫的 get_signed_cookie() 的原始碼,也不復雜,同樣是從self.COOKIES 中取出對應 key 的 value 值,然後使用對應的 salt 解密即可。

# 原始碼位置:django/http/request.py  
class HttpRequest:
    # ...
    
    def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None):
        """
        Attempt to return a signed cookie. If the signature fails or the
        cookie has expired, raise an exception, unless the `default` argument
        is provided,  in which case return that value.
        """
        try:
            cookie_value = self.COOKIES[key]
        except KeyError:
            if default is not RAISE_ERROR:
                return default
            else:
                raise
        try:
            value = signing.get_cookie_signer(salt=key + salt).unsign(
                cookie_value, max_age=max_age)
        except signing.BadSignature:
            if default is not RAISE_ERROR:
                return default
            else:
                raise
        return value
        
    # ...

接下來是涉及到建立 Cookie 的方法,我們需要查詢 HttpResponse 類或者相關的父類:

# 原始碼位置:django/http/response.py
class HttpResponseBase:
    # ...
    def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
                   domain=None, secure=False, httponly=False, samesite=None):
        """
        Set a cookie.

        ``expires`` can be:
        - a string in the correct format,
        - a naive ``datetime.datetime`` object in UTC,
        - an aware ``datetime.datetime`` object in any time zone.
        If it is a ``datetime.datetime`` object then calculate ``max_age``.
        """
        self.cookies[key] = value
        if expires is not None:
            if isinstance(expires, datetime.datetime):
                if timezone.is_aware(expires):
                    expires = timezone.make_naive(expires, timezone.utc)
                delta = expires - expires.utcnow()
                # Add one second so the date matches exactly (a fraction of
                # time gets lost between converting to a timedelta and
                # then the date string).
                delta = delta + datetime.timedelta(seconds=1)
                # Just set max_age - the max_age logic will set expires.
                expires = None
                max_age = max(0, delta.days * 86400 + delta.seconds)
            else:
                self.cookies[key]['expires'] = expires
        else:
            self.cookies[key]['expires'] = ''
        if max_age is not None:
            self.cookies[key]['max-age'] = max_age
            # IE requires expires, so set it if hasn't been already.
            if not expires:
                self.cookies[key]['expires'] = http_date(time.time() + max_age)
        if path is not None:
            self.cookies[key]['path'] = path
        if domain is not None:
            self.cookies[key]['domain'] = domain
        if secure:
            self.cookies[key]['secure'] = True
        if httponly:
            self.cookies[key]['httponly'] = True
        if samesite:
            if samesite.lower() not in ('lax', 'strict'):
                raise ValueError('samesite must be "lax" or "strict".')
            self.cookies[key]['samesite'] = samesite
            
    def set_signed_cookie(self, key, value, salt='', **kwargs):
        value = signing.get_cookie_signer(salt=key + salt).sign(value)
        return self.set_cookie(key, value, **kwargs)

    def delete_cookie(self, key, path='/', domain=None):
        # Most browsers ignore the Set-Cookie header if the cookie name starts
        # with __Host- or __Secure- and the cookie doesn't use the secure flag.
        secure = key.startswith(('__Secure-', '__Host-'))
        self.set_cookie(
            key, max_age=0, path=path, domain=domain, secure=secure,
            expires='Thu, 01 Jan 1970 00:00:00 GMT',
        )
        
    # ...

從上面的程式碼可以看到,最核心的方法是 set_cookie(),而刪除 cookie 和 設定加鹽的 cookie 方法最後都是呼叫 set_cookie() 這個方法。而這個方法也比較簡單,就是將對應的傳遞過來的引數值加到 self.cookies 這個字典中。最後我們思考下,難道就這樣就完了嗎?是不是還需要有一步是需要將 self.cookies 中的所有 key-value 值組成字串,放到頭部中,然後才返回給前端?事實上,肯定是有這一步的,程式碼如下。在用 “#” 號包圍起來的那一段程式碼正是將 self.cookies 中的所有 key-value 值組成字串形式,然後放到頭部的 “Set-Cookie” 中,正是有了這一步的動作,我們前面設定的 self.cookie 內部的 key-value 值才能真正生效。

# 原始碼位置:django/core/handlers/wsgi.py

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        ##############################################################################
        response_headers = [
            *response.items(),
            *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
        ]
        #############################################################################
        start_response(status, response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response

3.2 Django 中 Session 操作相關原始碼

Django 中和 Session 相關的程式碼較多,我們不去深入追究原始碼細節,主要是程式的執行過程。比如在哪裡設定的 request.session 值以及 session 相關資訊如何儲存到資料庫中的等等。我們先整體看下 Session 相關的程式碼位置:

圖片描述

這裡有幾個比較重要的地方,一個是 backends 目錄,下面是不同儲存 Session 資料方式的程式碼,如使用資料庫儲存、快取儲存或者檔案系統儲存等,每種儲存方式對應著一個程式碼檔案,裡面定義的類和方法都是一致的,這樣就可以無縫切換儲存引擎。

第二個是 middleware.py 檔案,我們先要了解下 Django 中 MIDDLEWARE 的工作流程,可以如下圖所示:
圖片描述

由於在 settings.py 中間設定了 Session 的中介軟體,所以 request 和 response 也會經歷 Session 中介軟體的這個流程,看 session 目錄下的 middleware.py 檔案的程式碼:

class SessionMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
        self.get_response = get_response
        engine = import_module(settings.SESSION_ENGINE)
        self.SessionStore = engine.SessionStore

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

    def process_response(self, request, response):
        """
        If request.session was modified, or if the configuration is to save the
        session every time, save the changes and set a session cookie or delete
        the session cookie if the session has been emptied.
        """
        try:
            accessed = request.session.accessed
            modified = request.session.modified
            empty = request.session.is_empty()
        except AttributeError:
            pass
        else:
            # First check if we need to delete this cookie.
            # The session should be deleted only if the session is entirely empty
            if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
                response.delete_cookie(
                    settings.SESSION_COOKIE_NAME,
                    path=settings.SESSION_COOKIE_PATH,
                    domain=settings.SESSION_COOKIE_DOMAIN,
                )
            else:
                if accessed:
                    patch_vary_headers(response, ('Cookie',))
                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 = http_date(expires_time)
                    # Save the session data and refresh the client cookie.
                    # Skip session save for 500 responses, refs #3881.
                    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
                            settings.SESSION_COOKIE_NAME,
                            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,
                            samesite=settings.SESSION_COOKIE_SAMESITE,
                        )
        return response

首先看 __init__() 方法中的設定了對應 Session 的儲存引擎,接下來的 process_request() 方法中我們可以看到 request.session = self.SessionStore(session_key) 這行語句正是給 request 的 session 屬性賦值,而這個值正是儲存引擎模組下的 SessionStore 類的例項。而對於 process_response() 方法,從程式碼中可以看到,它完成了 Session 資料的儲存以及將 session_id 值寫到 cookie 中去並返回給客戶端,呼叫的方法正是我們前面介紹到的 set_cookie() 方法。

接下來,我們看看其他幾個 python 檔案中的程式碼。例如下面的 models.py 定義了 Session 的 model 模型,包括欄位以及管理器:

# 原始碼位置:django/contrib/sessions/models.py
from django.contrib.sessions.base_session import (
    AbstractBaseSession, BaseSessionManager,
)

class SessionManager(BaseSessionManager):
    use_in_migrations = True

class Session(AbstractBaseSession):
    objects = SessionManager()

    @classmethod
    def get_session_store_class(cls):
        from django.contrib.sessions.backends.db import SessionStore
        return SessionStore

    class Meta(AbstractBaseSession.Meta):
        db_table = 'django_session'

從這段程式碼可以看到 Session 如果使用資料庫儲存資料的話,其建立的表名為:django_session,其欄位型別並不在這裡定義,而是繼承的父類 BaseSessionManager,這個類定義就在 base_session.py 檔案中:

class BaseSessionManager(models.Manager):
    def encode(self, session_dict):
        """
        Return the given session dictionary serialized and encoded as a string.
        """
        session_store_class = self.model.get_session_store_class()
        return session_store_class().encode(session_dict)

    def save(self, session_key, session_dict, expire_date):
        s = self.model(session_key, self.encode(session_dict), expire_date)
        if session_dict:
            s.save()
        else:
            s.delete()  # Clear sessions with no data.
        return s


class AbstractBaseSession(models.Model):
    session_key = models.CharField(_('session key'), max_length=40, primary_key=True)
    session_data = models.TextField(_('session data'))
    expire_date = models.DateTimeField(_('expire date'), db_index=True)

    objects = BaseSessionManager()

    class Meta:
        abstract = True
        verbose_name = _('session')
        verbose_name_plural = _('sessions')

    def __str__(self):
        return self.session_key

    @classmethod
    def get_session_store_class(cls):
        raise NotImplementedError

    def get_decoded(self):
        session_store_class = self.get_session_store_class()
        return session_store_class().decode(self.session_data)

從這裡就可以看到 django_session 表有3個欄位,分別是session_keysession_dataexpire_date。繼承這個基類必須要實現 get_session_store_class() 這個方法。另外 app.py 用於指明 session 應用名稱,exceptions.py 定義了兩個簡單的異常,serializers.py 的內容也比較簡單,僅僅使用 pickle 模組封裝了一個用於序列化的類:

import pickle

from django.core.signing import JSONSerializer as BaseJSONSerializer

class PickleSerializer:
    """
    Simple wrapper around pickle to be used in signing.dumps and
    signing.loads.
    """
    protocol = pickle.HIGHEST_PROTOCOL

    def dumps(self, obj):
        return pickle.dumps(obj, self.protocol)

    def loads(self, data):
        return pickle.loads(data)

JSONSerializer = BaseJSONSerializer

我們也可以簡單使用下這個類,其實就是熟悉 pickle 模組的 dumps()loads() 方法。

(django-manual) [root@server first_django_app]# python manage.py shell
Python 3.8.1 (default, Dec 24 2019, 17:04:00) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.sessions.serializers import PickleSerializer
>>> from hello_app.views import Member
>>> Member.objects.all().get(name='spyinx-1')
<Member: <spyinx-1, 18017715080>>
>>> m = Member.objects.all().get(name='spyinx-1')
>>> PickleSerializer().dumps(m)
b'\x80\x05\x95o\x01\x00\x00\x00\x00\x00\x00\x8c\x15django.db.models.base\x94\x8c\x0emodel_unpickle\x94\x93\x94\x8c\thello_app\x94\x8c\x06Member\x94\x86\x94\x85\x94R\x94}\x94(\x8c\x06_state\x94h\x00\x8c\nModelState\x94\x93\x94)\x81\x94}\x94(\x8c\x06adding\x94\x89\x8c\x02db\x94\x8c\x07default\x94ub\x8c\x02id\x94K\x05\x8c\x04name\x94\x8c\x08spyinx-1\x94\x8c\x03age\x94\x8c\x0225\x94\x8c\x03sex\x94K\x00\x8c\noccupation\x94\x8c\x07teacher\x94\x8c\tphone_num\x94\x8c\x0b18017715080\x94\x8c\x05email\x94\x8c\[email protected]\x94\x8c\x04city\x94\x8c\x08shenzhen\x94\x8c\rregister_date\x94\x8c\x08datetime\x94\x8c\x08datetime\x94\x93\x94C\n\x07\xe4\x04\t\x10\x18\n\x00\x00\x00\x94\x85\x94R\x94\x8c\x0cvip_level_id\x94N\x8c\x0f_django_version\x94\x8c\x062.2.11\x94ub.'
>>> m1 = PickleSerializer().loads(s)
>>> type(m1)
<class 'hello_app.models.Member'>
>>> m1.name
'spyinx-1'

最後,關於 Session 的引擎不用過多細究,裡面預設用到的是 backends/db.py。如果使用的是快取引擎,程式碼內容也是大同小異的。主要認真研究兩個類:

  • backends/base.py 中的 SessionBase 類。這個是所有 SessionStore 的基類,它具有的方法正是我們操作 session 的方法;
  • backends/db.py 中的 SessionStore 類。前面的 request.session 便是該類的一個例項,它的程式碼內容也是不復雜的,主要針對該種儲存方式需要完成特定的儲存(save())、刪除(delete()) 以及匯入(load())等方法。

到此為止,Django 中關於 Session 的程式碼就這麼多了。剩下的程式碼細節還需要各位去慢慢專研,也希望大家能認真鑽研 Django 的原始碼,多多思考,然後多多實踐。

4. 小結

本小節中我們講解了在 Django 中如何操作 Cookie 和 Session,並各給出了一個實戰案例。接下來我們學習了 Django 中操作 Cookie 和 Session 的方法的原始碼,在熟悉了這些程式碼之後,對於我們操作 Cookie 和 Session 會更加得心應手 。