Django中刷新页面为何会重复提交表单?该如何解决?
问题根因
你当前在登录验证失败后直接返回render渲染登录页,此时浏览器地址栏保留的是登录接口的POST请求记录,用户刷新页面时浏览器会自动重新提交上次的POST表单数据,导致login_attempt被重复累加。你之前尝试直接修改request对象的方案是无效的,无法改变浏览器端的请求记录,反而会破坏原有请求上下文导致CSRF校验失败。
解决方案:使用PRG(Post/Redirect/Get)模式
这是Web开发中避免表单重复提交的标准方案,所有POST请求处理完成后,无论成功失败都返回重定向响应,让浏览器主动发起GET请求加载目标页面,此时刷新页面只会重复GET请求,不会再次提交表单。
具体实现步骤:
- 引入Django内置的messages框架(用来传递跳转后的错误提示)
首先确保你项目的settings.py里已经开启了messages相关配置:
- 引入Django内置的messages框架(用来传递跳转后的错误提示)
INSTALLED_APPS = [ # ... 其他应用 'django.contrib.messages', ] MIDDLEWARE = [ # ... 其他中间件 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ] TEMPLATES = [ { # ... 其他模板配置 'OPTIONS': { 'context_processors': [ # ... 其他上下文处理器 'django.contrib.messages.context_processors.messages', ], }, }, ]
- 修改登录视图逻辑,所有POST处理分支都返回重定向,错误提示用messages存储,同时修复原有代码的几个隐性bug:
from django.contrib import messages from django.shortcuts import redirect, render from django.contrib.auth import login, authenticate def my_login(request): context = {} if request.method == 'POST': form = LoginForm(request.POST) # 先校验表单合法性再访问cleaned_data,注意is_valid是方法要加() if form.is_valid(): user_id = form.cleaned_data['user_id'] user_password = form.cleaned_data['password1'] try: usr = Users.objects.get(pk=user_id) except Users.DoesNotExist: messages.error(request, "User Not Found") # 跳转到登录页的GET路由,替换成你自己配置的登录页路由名 return redirect('my_login') # 你原有锁定账号的逻辑可以保留在这 if usr.login_attempt >=3: messages.error(request, "账号已被锁定,请稍后再试") return redirect('my_login') # 建议用Django自带的authenticate做密码校验,不要明文比对密码 if authenticate(request, username=usr.username, password=user_password): usr.login_attempt = 0 usr.save() # login方法第一个参数必须传入request对象,你原有代码漏传 login(request, usr) return redirect('dashboard') else: usr.login_attempt += 1 usr.save() messages.error(request, f"Attempts tried {usr.login_attempt}") return redirect('my_login') else: messages.error(request, "表单输入不合法,请重新输入") return redirect('my_login') # GET请求的时候初始化空表单 context['form'] = LoginForm() return render(request, 'login.html', context)
- 在
login.html模板中添加messages展示的代码:
- 在
{% if messages %} {% for message in messages %} <div class="alert alert-{{ message.tags }}"> {{ message }} </div> {% endfor %} {% endif %} <!-- 原有登录表单代码 -->
额外注意点
- 不要明文存储用户密码、直接比对明文密码,建议使用Django自带的用户认证体系完成密码的加密存储和校验。
- 调用
form.cleaned_data必须放在form.is_valid()调用之后,否则会抛出异常。
内容的提问来源于stack exchange,提问作者Premkumar
相关产品推荐
相关产品推荐

