Django Session过期不触发任何信号,清理由后台异步完成;django-user-sessions也不提供过期监听;可行方案是在中间件中主动检测expire_date并执行清理。

Session过期时Django本身不触发任何信号
Django的Session模型本身没有提供pre_delete、post_delete或自定义过期钩子,哪怕你用django.contrib.sessions默认后端(如数据库或缓存),过期清理都是后台异步完成的——比如python manage.py clearsessions定期跑,或者Redis自动TTL淘汰。这意味着:用户下次发请求时发现request.session为空,但你无法在“恰好过期那一刻”做任何事。
django-user-sessions不提供过期监听机制
django-user-sessions只是把Session和User做了外键关联,并添加了ip、user_agent等字段,它的Session模型继承自Django原生SessionBase,依然不带生命周期事件。它甚至没有重写save()或delete()来注入逻辑——所以别指望监听UserSession.objects.get(...).delete()就能捕获过期。
可行方案:在每次请求中主动检测并响应过期
真正能落地的做法,是在中间件或视图里判断当前会话是否“已失效但尚未被清除”,然后执行清理动作(如发通知、记日志、踢出设备)。关键点在于区分三种状态:
-
request.session.exists(session_key)返回False→ 会话已删除或从未存在 -
request.session.session_key存在但request.user.is_authenticated为False→ 很可能已过期(尤其配合SESSION_SAVE_EVERY_REQUEST=True) - 用
django-user-sessions查UserSession.objects.filter(session_key=...).exists()→ 可确认该会话是否还在数据库里(适用于DB后端)
示例中间件片段:
class SessionExpiryMonitorMiddleware:
def __init__(self, get_response):
self.get_response = get_response
<pre class="brush:php;toolbar:false;">def __call__(self, request):
if request.user.is_authenticated and request.session.session_key:
# 检查UserSession是否存在(仅DB后端有效)
from user_sessions.models import UserSession
try:
us = UserSession.objects.get(session_key=request.session.session_key)
if us.expire_date <p></p>注意clearsessions执行时机与竞态问题
python manage.py clearsessions是定时任务,不是实时触发器。如果你依赖它清理UserSession,那在两次执行之间,数据库里会残留大量expire_date 但未删除的记录。更麻烦的是:多个worker同时处理同一过期会话时,可能重复执行清理逻辑。解决办法只有两个:
- 加数据库层面的
SELECT ... FOR UPDATE锁(Django 4.2+ 支持select_for_update()) - 把清理动作移到异步任务(如Celery)中,用
session_key做唯一任务ID防重
别试图靠覆盖Session.save()或监听post_save来捕捉过期——过期不是保存导致的,而是时间到了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











