我有一个表单,用户可以在其中动态添加字段。提交此表单时,后端只会看到后端生成的字段
#forms.py
class ExpensesForm(FlaskForm):
expense_name = StringField('Expense_Item', validators=[DataRequired()])
cost = FloatField('Cost', validators=[DataRequired()])
due_date = DateField('Due Date', format='%Y-%m-%d', validators=[DataRequired()], default=datetime.datetime.today().date())
type = SelectField('Role', choices=[('mutual', 'Mutual'),
('personal#1', 'Personal #1'),
('personal#2', 'Personal #2')
])
我正在通过此表格
return render_template('index.html', form=form, ...)
从main.py
到index.html
所有4个字段都是通过以下方式生成的:
<form class="form-horizontal" id="main-form" enctype=multipart/form-data role="form" method="post" action="/">
<input type="hidden" name="count" value="1"/>
{{ form.csrf_token }}
{{ form.expense_name(placeholder="Expense Name", id="expense_1", value="") }}
{{ form.cost(placeholder="Cost", id="cost_1", class="cost", value="") }}
{{ form.due_date(id="due_date_1") }}
{{ form.type(placeholder="Type", id="type_1") }}
<button id="b1" class="btn btn-info add-more" type="button">+</button>
<small>Press + to add another set of fields.</small>
<br>
<hr>
<button class="btn btn-sm btn-success" type="submit">Post Expense</button>
</form>
在每个按钮按下后,JQuery代码片段会生成具有不同(唯一)ID的相同字段,作为最后一个#type_
ID之后的一行新字段。
当我按下“提交”按钮时,后端仅接收第一行,而不接收生成的行。
我在这里想念什么?
更新:
# main.py
@main_blueprint.route('/', methods=['GET', 'POST'])
def index():
dates = []
form = ExpensesForm(request.form)
if request.method == 'POST':
print(form.data)
# prints the following even when the browser sends more than 1 set of data:
# {'due_date': None, 'csrf_token': long_hash, 'expense_name': 'Electric', 'cost': 13.0, 'type': 'mutual'}
if form.validate_on_submit():
for n in range(len(form.expense_name.data)):
if form.expense_name.raw_data[n] != '':
data = Expenses(form.expense_name.raw_data[n].title(),
form.cost.raw_data[n],
datetime.datetime.strptime(form.due_date.raw_data[n], '%Y-%m-%d').date(),
form.type.raw_data[n].title(),
)
print(data)
db.session.add(data)
db.session.commit()
return redirect(url_for('main.index'))
expenses = db.session.query(Expenses).all()
# expenses_schema = ExpensesSchema()
# output = expenses_schema.dump(expenses).data
output = []
for i in expenses:
output.append(i.__dict__)
return render_template('index.html', form=form, expenses=output)
更新2
由于form.data
是字典,因此我无法使用与新字段匹配的名称。但是,即使我为添加的字段指定了唯一的名称,后端也只会显示带有print(form.data)
的初始表单字段,但是如果我这样做的话,
for k, v in request.form.items():
print(k, v)
我得到所有领域。对我来说似乎不是正确的方法。有什么想法吗?
答案 0 :(得分:2)
每个表单提交只能有一个表单结果。为了能够提交任意数量和未知数量的输入,您需要在WTForm's field enclosures的帮助下重组表单。
forms.py
from flask_wtf import FlaskForm
from wtforms import (
FieldList, FormField, DateField FloatField, StringField, SelectField)
from wtforms import Form as NoCsrfForm
class ExpenseItem(NoCsrfForm):
expense_name = StringField('Expense_Item', validators=[DataRequired()])
cost = FloatField('Cost', validators=[DataRequired()])
due_date = DateField('Due Date', format='%Y-%m-%d',
validators=[DataRequired()],
default=datetime.datetime.today().date())
type = SelectField('Role', choices=[
('mutual', 'Mutual'),
('personal#1', 'Personal #1'),
('personal#2', 'Personal #2'),
])
class ExpensesForm(FlaskForm):
"""A collection of expense items."""
items = FieldList(FormField(ExpenseItem), min_entries=1)
强烈建议您为所有字段名加上expense
,而不是expense_name
。
index.html
<form class="form-horizontal" id="main-form" enctype=multipart/form-data role="form" method="post" action="/">
<input type="hidden" name="count" value="1"/>
{{ form.hidden_tag() }}
{% for expense_item in form.items %}
{{ form.expense_name(placeholder="Expense Name", value="") }}
{{ form.cost(placeholder="Cost", class="cost", value="") }}
{{ form.due_date() }}
{{ form.type(placeholder="Type") }}
{% endfor %}
<button id="b1" class="btn btn-info add-more" type="button">+</button>
<small>Press + to add another set of fields.</small>
<br>
<hr>
<button class="btn btn-sm btn-success" type="submit">Post Expense</button>
</form>
请注意,HTML输入字段的id
属性必须遵循特定的模式。因此,对于通过单击+
按钮添加的每个新费用项目字段,您都需要为其输入字段的id
属性重新编号。
something.js
其他一切都比较容易。现在,您需要编写一段.js,以便在每次添加新费用项目时重新索引所有输入字段的id
属性。我使用Zepto Java脚本库完成了此任务。不好玩,我的.js很糟糕。我在这里能做的最好的事情就是粘贴整个内容,希望对您有所帮助。我知道这很混乱,但是我在一个课程中添加了多个 classes 。对于您来说,您需要 expense_item / expense_request 或其他配套工具:
// append class-box when new class link clicked
$("#new-class").click(function(event) {
appendClassBox('#classes', {{ newclass|tojson|safe }});
reindexNames('.class-box');
return false;
})
// remove class box when its "remove" link is clicked
$(document).on('click', '#remove-class', function(){
var $toremove = $(this).closest('.class-box');
$toremove.remove();
reindexNames('.class-box');
return false;
})
// add a new class-box
function appendClassBox(container, content) {
$(container).append(content);
// raise last and hence newest class box
raiseClassBox($(container).children().last())
return false;
}
function isNormalInteger(str) {
var n = ~~Number(str);
return String(n) === str && n >= 0;
}
// re-index class-box names
function reindexNames(class_name) {
var $oboxen = $(class_name);
$oboxen.each(function(index) {
// Get all the input fields in the class-box.
var $labels = $oboxen.eq(index).find('label')
var $inputs = $oboxen.eq(index).find(
'input, select, textarea')
// Update the index contained in the name attribute.
$inputs.each(function(idx) {
var $name = $inputs.eq(idx).attr('name').split('-');
// If number in name, grab from number onwards.
var $has_num = false
for (var part in $name) {
if (isNormalInteger($name[part])) {
$has_num = true
$name = $name.slice(part)
$name[0] = index
break
}
}
// Re-index.
if ($has_num == false) {
$name.unshift(index)
}
var $prefix = 'questions'
if (class_name == '.class-box') {
$prefix = 'classes'
}
$name.unshift($prefix)
if (idx > 0) {
$labels.eq(idx - 1).attr('for', $name.join('-'));
}
$inputs.eq(idx).attr('id', $name.join('-'));
$inputs.eq(idx).attr('name', $name.join('-'));
})
})
}
views.py
@main_blueprint.route('/', methods=['GET', 'POST'])
def index():
form = ExpensesForm()
# Iterate over a collection of new expense items.
if form.validate_on_submit():
for item in form.items.data:
print(item['expense_name'])
print(item['cost'])
print(item['due_date'])
print(item['type'])