You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Flask中使用WTForms获取动态表格表单的数据

嘿,刚好我之前做过类似的动态表单需求,这就给你拆解一下怎么用 WTForms 实现你要的功能:

核心思路:用 FieldList + FormField 处理动态重复字段

WTForms 专门提供了 FieldListFormField 组合来应对这种需要动态添加一组字段的场景——把每一行的「姓名+年龄」封装成一个子表单,再用 FieldList 来管理多个这样的子表单,后端就能直接把所有行的数据整合成列表啦。

1. 定义 WTForms 表单结构

先写两个表单类:一个子表单对应单一行的姓名和年龄,另一个主表单用 FieldList 包含这个子表单:

from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, FieldList, FormField, SubmitField
from wtforms.validators import DataRequired

# 子表单:对应表格的一行数据
class PersonRowForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired()])
    age = IntegerField('Age', validators=[DataRequired()])

# 主表单:管理多个子表单行
class DynamicTableForm(FlaskForm):
    # min_entries=1 确保页面加载时默认显示一行输入框
    person_rows = FieldList(FormField(PersonRowForm), min_entries=1)
    submit = SubmitField('Submit All Data')

2. 前端模板:渲染表单 + 动态添加/删除行

前端要做两件事:一是渲染初始的表单行,二是写 JS 实现点击「Add」按钮动态生成新行,还要遵循 WTForms 的字段命名规则(比如 person_rows-0-nameperson_rows-1-age 这种格式,后端才能正确解析)。

示例模板代码:

<form method="POST">
    {{ form.hidden_tag() }}
    <table>
        <thead>
            <tr>
                <td>{{ form.person_rows[0].name.label }} {{ form.person_rows[0].name }}</td>
                <td>{{ form.person_rows[0].age.label }} {{ form.person_rows[0].age }}</td>
                <td><button type="button" class="addRow">Add</button></td>
            </tr>
        </thead>
        <tbody id="tableBody">
            <!-- 如果有已存在的行(比如编辑场景),这里会自动渲染 -->
            {% for row in form.person_rows[1:] %}
            <tr>
                <td>{{ row.name.label }} {{ row.name }}</td>
                <td>{{ row.age.label }} {{ row.age }}</td>
                <td><button type="button" class="removeRow">Remove</button></td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    <br>
    {{ form.submit() }}
</form>

<script>
// 绑定添加行按钮事件
document.querySelector('.addRow').addEventListener('click', function() {
    const tbody = document.getElementById('tableBody');
    // 获取当前最大的行索引(用来生成新字段的命名)
    const lastRow = tbody.lastElementChild;
    let newIndex = 1;
    if (lastRow) {
        const lastInputName = lastRow.querySelector('input').name;
        newIndex = parseInt(lastInputName.split('-')[1]) + 1;
    }

    // 生成符合WTForms命名规则的新行
    const newRow = document.createElement('tr');
    newRow.innerHTML = `
        <td>
            <label for="person_rows-${newIndex}-name">Name</label>
            <input type="text" id="person_rows-${newIndex}-name" name="person_rows-${newIndex}-name" required>
        </td>
        <td>
            <label for="person_rows-${newIndex}-age">Age</label>
            <input type="number" id="person_rows-${newIndex}-age" name="person_rows-${newIndex}-age" required>
        </td>
        <td><button type="button" class="removeRow">Remove</button></td>
    `;

    tbody.appendChild(newRow);

    // 给新行的删除按钮绑定事件
    newRow.querySelector('.removeRow').addEventListener('click', function() {
        tbody.removeChild(newRow);
    });
});

// 给已有删除按钮绑定事件
document.querySelectorAll('.removeRow').forEach(btn => {
    btn.addEventListener('click', function() {
        this.parentElement.parentElement.remove();
    });
});
</script>

3. 后端接收并处理数据

在 Flask 视图函数里,直接通过表单对象就能拿到所有行的数据,轻松提取姓名和年龄列表:

from flask import Flask, render_template, redirect, url_for

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'

@app.route('/', methods=['GET', 'POST'])
def index():
    form = DynamicTableForm()
    if form.validate_on_submit():
        # 直接拿到所有行的列表数据,每个元素是包含name和age的字典
        all_rows = form.person_rows.data
        # 提取姓名和年龄到单独的列表
        names = [row['name'] for row in all_rows]
        ages = [row['age'] for row in all_rows]
        
        # 这里可以做你需要的逻辑,比如存数据库、打印输出等
        print("所有姓名:", names)
        print("所有年龄:", ages)
        
        return redirect(url_for('index'))
    return render_template('index.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)

额外注意点

  • 如果你实际用的是其他输入控件(比如下拉框、复选框),只需要修改 PersonRowForm 里的字段类型,前端动态生成时对应改成相应的 HTML 控件即可,命名规则保持不变。
  • FieldList 支持 min_entriesmax_entries 参数,可以限制最少/最多添加的行数。
  • 表单验证会自动覆盖所有动态添加的行,只要子表单里加了验证规则,空行或者不符合格式的内容会被拦截。

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

火山引擎 最新活动