找回密码
 立即注册
首页 业界区 业界 Django Session

Django Session

簧横 2025-9-30 11:48:22
Django 提供了一个强大的会话(Session)系统,用于在多个请求之间存储和检索用户特定的数据。
1、系统结构

1.1 代码结构
  1. django.contrib.sessions
  2. ├── middleware.py
  3. │   └── SessionMiddleware (处理请求/响应周期)
  4. ├── backends/
  5. │   ├── base.py
  6. │   │   └── SessionBase (抽象基类)
  7. │   ├── db.py
  8. │   │   └── SessionStore (数据库后端)
  9. │   ├── cache.py
  10. │   │   └── CacheSession (缓存后端)
  11. │   └── signed_cookies.py
  12. │       └── SignedCookieSession (Cookie后端)
  13. └── models.py
  14.     └── Session (数据库模型)
复制代码
1.2 工作原理


  • 当用户首次访问时,Django 创建一个唯一的 session ID
  • 这个 ID 通常通过 cookie 发送到客户端
  • 后续请求中,客户端会携带这个 ID,Django 通过它找到对应的 session 数据
  • Session 数据存储在服务器端(数据库、缓存等)
2、源码分析

2.1 SessionMiddleware


  • process_request: 处理请求,获取session_key并获取session赋值给request
  • process_response:处理响应,保存 session 并设置 Cookie
  1. class SessionMiddleware(MiddlewareMixin):
  2.     def __init__(self, get_response):
  3.         super().__init__(get_response)
  4.         # 导入 SessionStore 类
  5.         engine = import_module(settings.SESSION_ENGINE)
  6.         self.SessionStore = engine.SessionStore
  7.     def process_request(self, request):
  8.         # 从 Cookie 中获取 session_key
  9.         session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
  10.         # 创建 SessionStore 实例并赋值给 request.session
  11.         request.session = self.SessionStore(session_key)
  12.     def process_response(self, request, response):
  13.         """
  14.         处理响应,保存 session 并设置 Cookie
  15.         """
  16.         try:
  17.             accessed = request.session.accessed
  18.             modified = request.session.modified
  19.             empty = request.session.is_empty()
  20.         except AttributeError:
  21.              # 如果没有 session 属性,直接返回响应
  22.             return response
  23.         
  24.         if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
  25.             # 如果 session 为空但 Cookie 存在,删除 Cookie
  26.             response.delete_cookie(
  27.                 settings.SESSION_COOKIE_NAME,
  28.                 path=settings.SESSION_COOKIE_PATH,
  29.                 domain=settings.SESSION_COOKIE_DOMAIN,
  30.                 samesite=settings.SESSION_COOKIE_SAMESITE,
  31.             )
  32.             patch_vary_headers(response, ("Cookie",))
  33.         else:
  34.             # 如果 session 被访问过,设置 Vary: Cookie 头
  35.             if accessed:
  36.                 patch_vary_headers(response, ("Cookie",))
  37.             # 如果 session 被修改或设置了 SESSION_SAVE_EVERY_REQUEST,并且非空,则保存
  38.             if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
  39.                 if request.session.get_expire_at_browser_close():
  40.                     max_age = None
  41.                     expires = None
  42.                 else:
  43.                     max_age = request.session.get_expiry_age()
  44.                     expires_time = time.time() + max_age
  45.                     expires = http_date(expires_time)
  46.                 if response.status_code < 500:
  47.                     try:
  48.                         # 保存 session
  49.                         request.session.save()
  50.                     except UpdateError:
  51.                         raise SessionInterrupted(
  52.                             "The request's session was deleted before the "
  53.                             "request completed. The user may have logged "
  54.                             "out in a concurrent request, for example."
  55.                         )
  56.                     # 设置 Cookie
  57.                     response.set_cookie(
  58.                         settings.SESSION_COOKIE_NAME,
  59.                         request.session.session_key,
  60.                         max_age=max_age,
  61.                         expires=expires,
  62.                         domain=settings.SESSION_COOKIE_DOMAIN,
  63.                         path=settings.SESSION_COOKIE_PATH,
  64.                         secure=settings.SESSION_COOKIE_SECURE or None,
  65.                         httponly=settings.SESSION_COOKIE_HTTPONLY or None,
  66.                         samesite=settings.SESSION_COOKIE_SAMESITE,
  67.                     )
  68.         return response
复制代码
2.2 SessionBase 抽象基类关键方法
  1. class SessionBase:
  2.     """
  3.     Session 基类,定义了所有 Session 后端的通用接口
  4.     """
  5.     def __init__(self, session_key=None):
  6.         self._session_key = session_key
  7.         self.accessed = False
  8.         self.modified = False
  9.         self.serializer = import_string(settings.SESSION_SERIALIZER)
  10.    
  11.     def __getitem__(self, key):
  12.         return self._session[key]
  13.     def __setitem__(self, key, value):
  14.         self._session[key] = value
  15.         self.modified = True
  16.     def save(self, must_create=False):
  17.         """
  18.         必须由子类实现的具体保存逻辑
  19.         """
  20.         raise NotImplementedError(
  21.             "subclasses of SessionBase must provide a save() method"
  22.         )
  23.     def load(self):
  24.         """
  25.         必须由子类实现的数据加载逻辑
  26.         """
  27.         raise NotImplementedError(
  28.             "subclasses of SessionBase must provide a load() method"
  29.         )
  30.     def is_empty(self):
  31.         # 返回 session 是否为空
  32.         try:
  33.             return not self._session_key and not self._session_cache
  34.         except AttributeError:
  35.             return True
  36.     def _get_session(self, no_load=False):
  37.         """
  38.         获取 session 数据,如果没有加载则加载
  39.         """
  40.         self.accessed = True
  41.         try:
  42.             return self._session_cache
  43.         except AttributeError:
  44.             if self.session_key is None or no_load:
  45.                 self._session_cache = {}
  46.             else:
  47.                 self._session_cache = self.load()
  48.         return self._session_cache
  49.     def get_expiry_age(self, **kwargs):
  50.         """
  51.         获取 session 过期时间(秒)
  52.         """
  53.         try:
  54.             modification = kwargs["modification"]
  55.         except KeyError:
  56.             modification = timezone.now()
  57.         try:
  58.             expiry = kwargs["expiry"]
  59.         except KeyError:
  60.             expiry = self.get("_session_expiry")
  61.         if not expiry:
  62.             return self.get_session_cookie_age()
  63.         if not isinstance(expiry, (datetime, str)):
  64.             return expiry
  65.         if isinstance(expiry, str):
  66.             expiry = datetime.fromisoformat(expiry)
  67.         delta = expiry - modification
  68.         return delta.days * 86400 + delta.seconds
  69.     def get_expiry_date(self, **kwargs):
  70.         """
  71.         获取 session 过期日期
  72.         """
  73.         try:
  74.             modification = kwargs["modification"]
  75.         except KeyError:
  76.             modification = timezone.now()
  77.         try:
  78.             expiry = kwargs["expiry"]
  79.         except KeyError:
  80.             expiry = self.get("_session_expiry")
  81.         if isinstance(expiry, datetime):
  82.             return expiry
  83.         elif isinstance(expiry, str):
  84.             return datetime.fromisoformat(expiry)
  85.         expiry = expiry or self.get_session_cookie_age()
  86.         return modification + timedelta(seconds=expiry)
  87.     def get_expire_at_browser_close(self):
  88.         """
  89.         获取是否在浏览器关闭时过期
  90.         """
  91.         if (expiry := self.get("_session_expiry")) is None:
  92.             return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
  93.         return expiry == 0
  94.     def set_expiry(self, value):
  95.         """
  96.         设置 session 过期时间
  97.         """
  98.         if value is None:
  99.             # 使用全局设置
  100.             try:
  101.                 del self["_session_expiry"]
  102.             except KeyError:
  103.                 pass
  104.             return
  105.         if isinstance(value, timedelta):
  106.             value = timezone.now() + value
  107.         if isinstance(value, datetime):
  108.             value = value.isoformat()
  109.         self["_session_expiry"] = value
  110.     def flush(self):
  111.         """
  112.         清空 session 并生成新的 session_key
  113.         """
  114.         self.clear()
  115.         self.delete()
  116.         self._session_key = None
  117.     def cycle_key(self):
  118.         """
  119.         生成新的 session_key,保持 session 数据
  120.         """
  121.         data = self._session
  122.         key = self.session_key
  123.         self.create()
  124.         self._session_cache = data
  125.         if key:
  126.             self.delete(key)
复制代码
2.3 数据库Session后端源码
  1. class SessionStore(SessionBase):
  2.     """
  3.     数据库支持的 session 存储
  4.     """
  5.     def __init__(self, session_key=None):
  6.         super().__init__(session_key)
  7.     @classmethod
  8.     def get_model_class(cls):
  9.         # 避免循环导入,获取Session数据库模型定义
  10.         from django.contrib.sessions.models import Session
  11.         return Session
  12.     @cached_property
  13.     def model(self):
  14.         return self.get_model_class()
  15.     def _get_session_from_db(self):
  16.         # 从数据库获取session实例
  17.         return self.model.objects.get(
  18.                 session_key=self.session_key, expire_date__gt=timezone.now()
  19.             )
  20.     def load(self):
  21.         # 解码session
  22.         s = self._get_session_from_db()
  23.         return self.decode(s.session_data) if s else {}
  24.     def exists(self, session_key):
  25.         # 判断session是否存在
  26.         return self.model.objects.filter(session_key=session_key).exists()
  27.     def create(self):
  28.         while True:
  29.             self._session_key = self._get_new_session_key()
  30.             try:
  31.                 # 保存入库
  32.                 self.save(must_create=True)
  33.             except CreateError:
  34.                 # Key wasn't unique. Try again.
  35.                 continue
  36.             self.modified = True
  37.             return
  38.     def create_model_instance(self, data):
  39.         """
  40.         """
  41.         return self.model(
  42.             session_key=self._get_or_create_session_key(),
  43.             session_data=self.encode(data),
  44.             expire_date=self.get_expiry_date(),
  45.         )
  46.     def save(self, must_create=False):
  47.         """
  48.         使用事务保存session
  49.         """
  50.         if self.session_key is None:
  51.             return self.create()
  52.         data = self._get_session(no_load=must_create)
  53.         obj = self.create_model_instance(data)
  54.         using = router.db_for_write(self.model, instance=obj)
  55.         with transaction.atomic(using=using):
  56.             obj.save(
  57.                 force_insert=must_create, force_update=not must_create, using=using
  58.             )
  59.     def delete(self, session_key=None):
  60.         # 删除
  61.         self.model.objects.get(session_key=session_key).delete()
  62.     @classmethod
  63.     def clear_expired(cls):
  64.         # 清理过期session
  65.         cls.get_model_class().objects.filter(expire_date__lt=timezone.now()).delete()
复制代码
3、配置和使用

3.1 配置

在 settings.py 中配置 Session
  1. # Session 引擎
  2. SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 数据库
  3. # SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 缓存
  4. # SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'  # 缓存+数据库
  5. # SESSION_ENGINE = 'django.contrib.sessions.backends.file'  # 文件
  6. # SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'  # 签名Cookie
  7. # Session Cookie 名称
  8. SESSION_COOKIE_NAME = 'sessionid'
  9. # Session Cookie 过期时间(秒)
  10. SESSION_COOKIE_AGE = 1209600  # 2周
  11. # 是否在浏览器关闭时过期
  12. SESSION_EXPIRE_AT_BROWSER_CLOSE = False
  13. # 是否每次请求都保存 Session
  14. SESSION_SAVE_EVERY_REQUEST = False
  15. # Session Cookie 路径
  16. SESSION_COOKIE_PATH = '/'
  17. # Session Cookie 域名
  18. SESSION_COOKIE_DOMAIN = None
  19. # 是否只通过 HTTPS 传输
  20. SESSION_COOKIE_SECURE = False
  21. # 是否阻止 JavaScript 访问
  22. SESSION_COOKIE_HTTPONLY = True
  23. # SameSite 属性
  24. SESSION_COOKIE_SAMESITE = 'Lax'
  25. # Session 序列化器
  26. SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
复制代码
3.2 基本使用
  1. from django.shortcuts import render, redirect
  2. from django.http import HttpResponse
  3. def set_session(request):
  4.     """设置 Session 值"""
  5.     # 设置简单值
  6.     request.session['username'] = 'john'
  7.     request.session['user_id'] = 123
  8.    
  9.     # 设置复杂数据结构
  10.     request.session['user_data'] = {
  11.         'name': 'John Doe',
  12.         'email': 'john@example.com',
  13.         'preferences': {
  14.             'theme': 'dark',
  15.             'language': 'en'
  16.         }
  17.     }
  18.    
  19.     # 设置过期时间(300秒后过期)
  20.     request.session.set_expiry(300)
  21.    
  22.     return HttpResponse("Session values set")
  23. def get_session(request):
  24.     """获取 Session 值"""
  25.     username = request.session.get('username', 'Guest')
  26.     user_id = request.session.get('user_id')
  27.    
  28.     # 获取整个 Session 数据
  29.     session_data = dict(request.session.items())
  30.    
  31.     return HttpResponse(f"Username: {username}, User ID: {user_id}")
  32. def delete_session(request):
  33.     """删除 Session 值"""
  34.     # 删除特定键
  35.     if 'username' in request.session:
  36.         del request.session['username']
  37.    
  38.     # 使用 pop 方法
  39.     user_id = request.session.pop('user_id', None)
  40.    
  41.     # 清空整个 Session
  42.     request.session.clear()
  43.    
  44.     return HttpResponse("Session values deleted")
  45. def flush_session(request):
  46.     """完全清空 Session 并生成新 Session ID"""
  47.     request.session.flush()
  48.     return HttpResponse("Session flushed")
复制代码
3.3 过期时间
  1. def set_temp_session(request):
  2.     # 设置临时数据,10分钟后过期
  3.     request.session['temp_data'] = '这是临时数据'
  4.     request.session.set_expiry(600)  # 600秒=10分钟
  5.     return HttpResponse("临时session数据已设置,10分钟后过期")
  6. def set_browser_close_session(request):
  7.     # 设置浏览器关闭时过期的session
  8.     request.session['temporary'] = '浏览器关闭后消失'
  9.     request.session.set_expiry(0)  # 0表示浏览器关闭时过期
  10.     return HttpResponse("此session将在浏览器关闭时过期")
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册