You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中刷新页面为何会重复提交表单?该如何解决?

问题根因

你当前在登录验证失败后直接返回render渲染登录页,此时浏览器地址栏保留的是登录接口的POST请求记录,用户刷新页面时浏览器会自动重新提交上次的POST表单数据,导致login_attempt被重复累加。你之前尝试直接修改request对象的方案是无效的,无法改变浏览器端的请求记录,反而会破坏原有请求上下文导致CSRF校验失败。

解决方案:使用PRG(Post/Redirect/Get)模式

这是Web开发中避免表单重复提交的标准方案,所有POST请求处理完成后,无论成功失败都返回重定向响应,让浏览器主动发起GET请求加载目标页面,此时刷新页面只会重复GET请求,不会再次提交表单。

具体实现步骤:

    1. 引入Django内置的messages框架(用来传递跳转后的错误提示)
      首先确保你项目的settings.py里已经开启了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',
            ],
        },
    },
]
    1. 修改登录视图逻辑,所有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)
    1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 15:39:03