从Django 2.0升级至4.2后调用login()函数出现会话提前删除错误
问题场景
将Django从2.0升级到4.2后,在权限类的handle_no_permission方法中调用login()函数时,出现以下错误提示:
The request's session was deleted before the request completed. The user may have logged out in a concurrent request, for example.
但实际用户登录是成功的——login()调用前后self.request.user.is_authenticated分别返回False和True,功能逻辑正常,仅抛出该错误。
核心原因
Django 3.1+对session管理增加了更严格的状态检查:当请求中存在先删除旧session、再创建新session的操作时(比如此处登录时会清理原匿名用户的session,再生成登录用户的session),如果后续代码继续触发请求生命周期的session校验(比如调用父类的handle_no_permission),就会触发这个错误提示。
旧版本Django(2.0)没有这类严格校验,所以不会出现该问题。
解决方案
方案1:登录成功后直接返回响应,终止后续流程
当前代码在登录成功后仍调用super().handle_no_permission(),父类方法会继续执行权限失败的后续逻辑,和已完成的登录操作产生session状态冲突。修改后代码如下:
def handle_no_permission(self): if self.request.user.is_authenticated and not self.test_func() and not self.is_source_email(): self.raise_exception = True if all(x in self.request.GET.keys() for x in ["token", "source"]): if self.is_source_email(): token_from_url = self.request.GET.get("token") deficiency_id = self.kwargs.get('deficiency_id') try: login_token = ProviderLoginToken.objects.get(key=token_from_url, deficiency_id=deficiency_id) if login_token.active: user = login_token.provider.user user.backend = "django.contrib.auth.backends.ModelBackend" login(self.request, user, backend=user.backend) # 登录成功后直接返回目标页面/重定向,不再执行父类方法 return redirect("your_target_url_name") # 替换为实际的成功跳转地址 else: return render(self.request, "500_invalid_quick_amend_link.html") except ProviderLoginToken.DoesNotExist: return render(self.request, "500_invalid_quick_amend_link.html") return super().handle_no_permission()
方案2:手动清理旧session后再登录
如果必须保留后续流程,可以在调用login()前主动清理当前无效session,避免状态冲突:
# 在login()调用前添加以下代码 if self.request.session.session_key: self.request.session.flush() # 原登录逻辑 login(self.request, user, backend=user.backend)
关键说明
这个错误属于Django新版本的session校验机制带来的"误报"——并非真的有并发登出操作,而是单请求内的session状态变更触发了校验逻辑。只要确保登录操作完成后,不再触发可能干扰session状态的后续权限逻辑,就能解决问题。
内容的提问来源于stack exchange,提问作者Muhammad Huzaifa

