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

Python+Flask实现外部文件生成问卷表单时触发BadRequestKeyError的问题求助

解决Flask中Werkzeug BadRequestKeyError的问题

你遇到的BadRequestKeyError本质是前端表单控件的name属性和后端获取参数的key完全不匹配,加上代码里还有几个逻辑bug,咱们一步步来修复:

核心问题分析

  • 前端HTML里,单选按钮的name是question{{qnum}}(比如question0、question1,因为Jinja的enumerate默认从0开始)
  • 但后端calculate函数里却用问题的完整文本(比如Over the past month, have you been procrastinating...)作为key去request.form里取值,这显然找不到对应的参数,直接触发KeyError

具体修复步骤

1. 修正前端表单的单选按钮命名和值

先把前端的循环逻辑改得更清晰,确保name和后端能对应,同时让单选按钮的value直接是用户选择的分数(1-10):

{%extends "base.html" %}
{% block title %}Take the Test{% endblock %}
{% block content %}
<head>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<div class="row vh-100">
<div class="col-1"></div>
<div class="col-6">
<h3>About The Test</h3>
<p class="blue">This test will ask you if you seem to have any of the different symptoms of burn out, anxiety and extreme stress. Consider these questions as abnormal things you usually don’t do – like if you usually procrastinate and know it’s part of your personality, think if you have procrastinated more than usual. This test has been designed for teenagers, but is suitable for all ages. </p>
</div>
<div class="col-1"></div>
<div class="col-3">
<i class="glyphicon glyphicon-star" aria-hidden="true"></i><p class="blue">Test questions</p>
<i class="glyphicon glyphicon-star-empty" aria-hidden="true"></i><p class="blue">More test questions</p>
<i class="glyphicon glyphicon-star-empty" aria-hidden="true"></i><p class="blue">Your Results</p>
</div>
<div class="col-1"></div>
</div>
<div class="row vh-500">
<div class="col-2"></div>
<div class="col-8">
<!-- 修正表单提交路径为后端的results路由 -->
<form action='/results' method='POST'>
<ol>
{% for q_idx, qtext in enumerate(q, start=1) %}
<li>{{qtext}}</li>
<div>
{% for score in answers %}
    <label style="margin-right: 15px;">
        {{score}}
        <input type='radio' value='{{score}}' name="question-{{q_idx}}" required/>
    </label>
{% endfor %}
</div>
{% endfor %}
<input type="submit" value=" See Results "/>
</ol>
</form>
</div>
<div class="col-2"></div>
</div>
</div>
</body>
{% endblock %}

这里做了几个关键优化:

  • 用enumerate(q, start=1)让问题序号从1开始,name设为question-{{q_idx}}(比如question-1对应第一个问题)
  • 单选按钮的value直接用分数score(1-10),不用索引,后端拿到就能直接用
  • 加了required属性,强制用户必须选完所有问题才能提交
  • 修正了表单提交路径为/results(之前你写的/secondtestpage没有对应路由)

2. 重构后端的calculate函数

现在后端要根据前端的name格式来取值,同时修复循环里重置分数的bug:

def calculate(start_num, end_num):
    mental_issue = 0  # 把初始化放在循环外面,避免每次循环重置分数
    for q_num in range(start_num, end_num + 1):
        # 用.get()方法安全取值,避免KeyError
        answered_score_str = request.form.get(f"question-{q_num}")
        if not answered_score_str:
            flash(f"请完成第{q_num}个问题的回答")
            return redirect(url_for('takethetest'))
        
        answered_score = int(answered_score_str)
        # 问题的索引从0开始,所以q_num - 1对应列表里的位置
        q_index = q_num - 1
        # 根据奇偶索引计算分数
        if q_index % 2 != 0:
            mental_issue += answered_score * 10
        else:
            mental_issue += (10 - answered_score) * 10
    return mental_issue

核心改进:

  • 用request.form.get()替代直接下标访问,避免KeyError,还能处理未提交的情况
  • 把mental_issue的初始化移到循环外,确保累加所有问题的分数(之前的代码每次循环都会重置,最后只返回最后一个问题的分数)
  • 用问题序号(1-15)来对应前端的name,和前端完全匹配

3. 修正results路由的调用

之前你调用calculate的参数逻辑不对,现在改成正确的起止序号:

@app.route("/results", methods=["POST"])
def results():
    if request.method == "POST":
        try:
            burn_out = calculate(1, 5)
            stress = calculate(6, 10)
            anxiety = calculate(11, 15)
            return render_template("results.html", b=burn_out, s=stress, a=anxiety)
        except ValueError:
            flash("请选择有效的分数(1-10)")
            return redirect(url_for('takethetest'))
    else:
        # 如果是GET请求,直接跳回测试页面
        return redirect(url_for('takethetest'))

加了异常处理,防止用户提交非法值(比如非数字)。

4. 额外优化:清理冗余代码

你代码里的firstpage_questions和firstquestions深拷贝是冗余的,直接用一个列表就行:

firstquestions = []
with open("firstquestions.txt", "r") as file:
    for line in file:
        cleaned_line = line.rstrip("\n")
        if cleaned_line:  # 跳过空行
            firstquestions.append(cleaned_line)

测试验证

现在运行代码,提交表单后:

  • 前端的每个单选按钮name是question-1到question-15
  • 后端calculate函数用对应的key取值,完全匹配
  • 分数会正确累加,不会再触发BadRequestKeyError

内容的提问来源于stack exchange,提问作者Ari Mulligan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 22:54:06