从前端向 FlaskForm 添加多个 WTF 字段

问题描述 投票:0回答:1

我想制作一个字段数量不固定的表单,以便用户可以根据需要添加任意数量的新字段。

如何在不提前知道 n 个字段的情况下获取 Python 端的所有表单数据(通过 WTForms)?

forms.py

class Form(FlaskForm):
    field = StringField('field')
    submit = SubmitField('submit')

HTML

<!-- I want my form to be viewed in Bootstrap Modal. -->

<div class="modal" tabindex="-1">
  <div class="modal-body">

    <form name="create-form" method="POST">
      <div class="input-group">
        {{ form.hidden_tag() }}
        {{ form.field(class="form-control") }}
        <!-- The place to insert additional fields -->
    
        {{ form.submit(class="btn btn-outline-secondary) }}
      </div>
    </form>
    <button id="add" type="button" class="btn btn-outline-secondary">Add</button>

  </div>
</div>

<template id="new-field">
  {{ form.field(class="form-control") }}
</template>

JS

modal.querySelector('#add').addEventListener('click', (ev) => { 
    modal.querySelector(/*Place to insett*/).before(/*new field from <template>*/);
});

modal.querySelector('form').addEventListener('submit', async (ev) => {
    ev.preventDefault();
    let form = modal.querySelector('form');
    let resp = await fetch('/ajax', {method: 'POST', body: new FormData(form)});
});
                       
routes.py

@bp.route('/ajax', methods=['GET', 'POST'])
def ajax():
    # request.form contains all values 
    form = Form()
    
    if form.validate():
        # form.data has the only one value (from first field)
        return {}
    return {}

是的,似乎我需要为每个输入提供唯一的 wtf 字段。好吧,回到绘图板。

forms.py

class Form(FlaskForm):
    field = StringField('field')
    submit = SubmitField('submit')

    def add_filed(self, i):
        field = StringField(f'field{i}')
        setattr(self.__class__, f'field{i}', field)
        return self.__class__()
            
HTML

<template id="new-field">
  {{ form.add_field(7)|attr("field7")(class="form-control") }}
</template>

什么也没有。

TypeError: 'UnboundField' object is not callable

还有一次尝试:

modal.querySelector('#add').addEventListener('click', async (ev) => {
    // At this point I'm sending ajax-request to call Form.add_field(7)
    // on the Python side, bu as result I got {'field': 'value', 'field7': None}.
    modal.querySelector(/*Place to insett*/).before(/*new field from <template>*/);
});

我错过了什么?

我读了很多有关此的主题,但我不明白。

python flask wtforms
1个回答
0
投票

以下解决方案使用具有至少一个字段的

FieldList
。在列表中,ID 和名称属性由连续的数字补充。
使用 JavaScript,现在可以克隆由标签和字段组成的最后一组。可以提取属性,增加数量,添加新的组元素。
可以使用
data
FieldList
属性以及迭代其包含的字段来查询输入。

from flask import (
    Flask,
    render_template,
)
from flask_wtf import FlaskForm
from wtforms import FieldList, StringField, SubmitField 

app = Flask(__name__)
app.secret_key = 'your secret here'

class MyForm(FlaskForm):
    fields = FieldList(StringField('Field'), min_entries=1)
    submit = SubmitField('Submit')

@app.route('/', methods=['GET', 'POST'])
def index():
    form = MyForm()
    if form.validate_on_submit(): 
        print(form.fields.data)
    return render_template('index.html', **locals())
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Index</title>
</head>
<body>

    <form method="post">
        {{ form.hidden_tag() }}
        {% for field in form.fields -%}
        <div>
            {{ field.label() }}
            {{ field() }}
        </div>
        {% endfor -%}
        {{ form.submit() }}
    </form>
    <button id="add" type="button" class="btn btn-outline-secondary">Add</button>

    <script>
        (function() {
            document.getElementById('add').addEventListener('click', () => {
                const fieldGroup = document.querySelector('form div:last-of-type')
                const newFieldGroup = fieldGroup.cloneNode(true);
                newFieldGroup.querySelectorAll('input[name^="fields-"]').forEach(field => {
                    const nameAttr = field.name, 
                        newNameAttr = nameAttr.replace(
                            /^fields-(\d+)$/, 
                            (match, p1) => `fields-${parseInt(p1)+1}`
                        );
                    field.id = field.name = newNameAttr; 
                    field.value = '';

                    const label = newFieldGroup.querySelector(`label[for="${nameAttr}"]`);
                    label.setAttribute('for', newNameAttr);
                });
                fieldGroup.after(newFieldGroup)
            });
        })();
    </script>
</body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.