Django会话超时登录后如何固定重定向至首页而非原访问页
当用户在任意页面停留到会话超时,点击跳转到其他页面的链接时,会被自动重定向到登录页;默认情况下完成登录后,系统会跳转到用户之前尝试访问的目标页,但需求是所有场景下登录成功后都固定跳转到首页home.html。目前已经在settings.py中配置了LOGIN_REDIRECT_URL = 'home',但配置没有生效。
现有项目配置
项目所有视图都要求登录后才能访问:
- 类视图统一用装饰器加登录校验:
@method_decorator([login_required], name='dispatch') class QuestionListView(PermissionRequiredMixin, ListView): # 业务逻辑代码
- 函数视图直接用
@login_required装饰器校验,首页视图示例:
@login_required def home(request): # 首页业务逻辑
项目全程使用Django原生auth模块,没有自定义登录视图,登录模板login.html表单代码如下:
<form method="post"> {% csrf_token %} {{ form|crispy }} <a class="button secondaryAction" href="{% url 'password_reset' %}">Forgot Password?</a> <button type="submit" class="btn btn-secondary mt-2 pl-4">Log In</button> </form>
查看Django框架django.contrib.auth.views模块源码,重定向逻辑在RedirectURLMixin类中实现:
class RedirectURLMixin: next_page = None redirect_field_name = REDIRECT_FIELD_NAME success_url_allowed_hosts = set() def get_success_url(self): return self.get_redirect_url() or self.get_default_redirect_url() def get_redirect_url(self): """Return the user-originating redirect URL if it's safe.""" redirect_to = self.request.POST.get( self.redirect_field_name, self.request.GET.get(self.redirect_field_name) ) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts=self.get_success_url_allowed_hosts(), require_https=self.request.is_secure(), ) return redirect_to if url_is_safe else "" def get_success_url_allowed_hosts(self): return {self.request.get_host(), *self.success_url_allowed_hosts} def get_default_redirect_url(self): """Return the default redirect URL.""" if self.next_page: return resolve_url(self.next_page) raise ImproperlyConfigured("No URL to redirect to. Provide a next_page.")
之前的判断完全准确:会话超时后点击链接触发登录跳转时,@login_required装饰器会自动在登录页URL上拼接next参数,记录用户原本要访问的地址。而原生登录视图的逻辑是优先读取合法的next参数作为跳转地址,只有next不存在或非法时,才会读取LOGIN_REDIRECT_URL配置的默认地址,所以只修改这个配置没法覆盖带next参数的登录场景。
需求是临时让所有登录成功的请求统一跳首页,让用户能看到首页的公告横幅,不需要保留原跳转逻辑,两种实现方式二选一即可:
方案1:修改模板固定next参数(改动最小,适合临时需求)
Django登录模板如果没有显式定义next表单字段,当URL带next参数时,框架会自动生成隐藏input提交这个值。只要在表单里手动加一个固定值为首页地址的next隐藏域,就会覆盖URL携带的参数:
在login.html的表单中,{% csrf_token %}后添加一行:
<input type="hidden" name="next" value="{% url 'home' %}">
修改后表单完整结构:
<form method="post"> {% csrf_token %} <!-- 新增固定next参数的隐藏域 --> <input type="hidden" name="next" value="{% url 'home' %}"> {{ form|crispy }} <a class="button secondaryAction" href="{% url 'password_reset' %}">Forgot Password?</a> <button type="submit" class="btn btn-secondary mt-2 pl-4">Log In</button> </form>
这个方案不需要改任何视图、路由代码,后续公告下线要恢复原跳转逻辑,直接删掉这行隐藏域即可。
方案2:重写登录视图的跳转逻辑(扩展性更好)
如果后续还要扩展登录相关逻辑(比如记录登录时间、登录IP),可以继承原生LoginView重写跳转方法,强制返回首页地址:
- 在
views.py中自定义登录视图:
from django.contrib.auth.views import LoginView from django.urls import reverse_lazy class FixedRedirectLoginView(LoginView): def get_success_url(self): # 忽略所有next参数,统一跳首页 return reverse_lazy('home')
- 修改根路由
urls.py中的登录路由,将原来指向原生LoginView的配置替换为自定义视图:
from django.urls import path from yourapp.views import FixedRedirectLoginView urlpatterns = [ # 其他原有路由... # 替换原有的login路由配置 path('accounts/login/', FixedRedirectLoginView.as_view(template_name='login.html'), name='login'), # 其余auth相关路由无需修改 ]
两种方案都不会影响现有
@login_required装饰器、权限校验的正常运行,登录后所有用户都会先进入首页看到公告,完全满足需求。
内容的提问来源于stack exchange,提问作者Doug Smith

