如何使用 FormFields 的 WTForms FieldList?
Posted
技术标签:
【中文标题】如何使用 FormFields 的 WTForms FieldList?【英文标题】:How to use a WTForms FieldList of FormFields? 【发布时间】:2015-07-19 05:56:39 【问题描述】:我正在使用Flask 构建一个网站,其中我使用WTForms。在表单中,我现在想使用 FormFields 的 FieldList,如下所示:
class LocationForm(Form):
location_id = StringField('location_id')
city = StringField('city')
class CompanyForm(Form):
company_name = StringField('company_name')
locations = FieldList(FormField(LocationForm))
所以为了让人们能够进入一家有两个地点的公司(稍后会动态添加地点),我在前台这样做:
<form action="" method="post" role="form">
companyForm.hidden_tag()
companyForm.company_name()
locationForm.location_id()
locationForm.city()
locationForm.location_id()
locationForm.city()
<input type="submit" value="Submit!" />
</form>
所以在提交时我打印位置:
print companyForm.locations.data
但我明白了
['location_id': u'', 'city': u'']
我可以使用 locationForm 打印第一个位置的值(见下文),但我仍然不知道如何获取第二个位置的数据。
print locationForm.location_id.data
print locationForm.city.data
所以位置列表确实有一个空值字典,但是:
-
为什么位置列表只有一个,而不是两个字典?
为什么位置字典中的值是空的?
有人知道我在这里做错了什么吗?欢迎所有提示!
【问题讨论】:
【参考方案1】:这是一个老问题,但仍然是个好问题。
我想添加一个基于 Flask 的玩具数据库示例(只是一个字符串列表),重点放在 Python 部分 - 如何使用可变数量的子表单初始化表单以及如何处理发布的数据。
这是example.py
文件:
import flask
import wtforms
import flask_wtf
app = flask.Flask(__name__)
app.secret_key = 'fixme!'
# not subclassing from flask_wtf.FlaskForm
# in order to avoid CSRF on subforms
class EntryForm(wtforms.Form):
city = wtforms.fields.StringField('city name:')
delete = wtforms.fields.BooleanField('delete?')
class MainForm(flask_wtf.FlaskForm):
entries = wtforms.fields.FieldList(wtforms.fields.FormField(EntryForm))
submit = wtforms.fields.SubmitField('SUBMIT')
city_db = "Graz Poprad Brno Basel Rosenheim Torino".split() # initial value
@app.route("/", methods=['POST'])
def demo_view_function_post():
global city_db
form = MainForm()
if form.validate_on_submit():
city_db = [
entry['city'] for entry in form.entries.data
if entry['city'] and not entry['delete']]
return flask.redirect(flask.url_for('demo_view_function_get'))
# handle the validation error, i.e. flash a warning
return flask.render_template('demo.html', form=form)
@app.route("/")
def demo_view_function_get():
form = MainForm()
entries_data = ['city': city, 'delete': False for city in city_db]
entries_data.append('city': '', 'delete': False) # "add new" placeholder
form.process(data='entries': entries_data)
return flask.render_template('demo.html', form=form)
这是demo.html
文件:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Demo</title>
</head>
<body>
<h1>Subform demo</h1>
<p>Edit names / mark for deletion / add new</p>
<form method="post">
form.csrf_token()
% for entry in form.entries %
% if loop.last %
<div>Add new:</div>
% endif %
<div>
entry.city.label entry.city()
entry.delete() entry.delete.label
</div>
% endfor %
form.submit()
</form>
</body>
运行:FLASK_APP=example flask run
【讨论】:
【参考方案2】:对于初学者,FieldList 有一个名为 min_entries
的参数,它将为您的数据腾出空间:
class CompanyForm(Form):
company_name = StringField('company_name')
locations = FieldList(FormField(LocationForm), min_entries=2)
这将以您需要的方式设置列表。接下来,您应该直接从 locations
属性呈现字段,以便正确生成名称:
<form action="" method="post" role="form">
companyForm.hidden_tag()
companyForm.company_name()
companyForm.locations()
<input type="submit" value="Submit!" />
</form>
查看呈现的 html,输入的名称应该类似于 locations-0-city
,这样 WTForms 就会知道哪个是哪个。
或者,对于元素的自定义渲染,可以这样做
% for l in companyForms.locations %
l.form.city
% endfor %
(仅在 wtforms 中,l.city
是 l.form.city
的简写。但是,该语法似乎与 Jinja 冲突,因此有必要在模板中使用显式的 l.form.city
。)
现在准备提交的数据,只需创建 CompanyForm
并遍历位置:
for entry in form.locations.entries:
print entry.data['location_id']
print entry.data['city']
【讨论】:
感谢您的回答。这确实使我能够在 html 中创建字段。还有一个问题:companyForm.locations()
创建了一个 <ul>
,其中包含一个包含每个位置输入的表。但我显然想自己创建格式,以便它融入我的设计。您知道如何在没有围绕它的表格的情况下创建单独的输入,以便我可以自己进行样式设置吗?
嗯...如果您需要控制体验,总有一个地方您必须手动接管,在这种情况下,您真正需要的是输入正确的名称,所以去吧,只要你有<input name=locations-0-city>
,一切都会好起来的:)
啊,我已经这么想了。至少现在我知道输入需要有什么name
。感谢您的帮助!
@kramer65 如果您还没有在其他地方找到答案,您可以在模板中使用 Jinja 迭代 companyForm.locations()
- 各个项目将只是相关的 HTML 输入,没有任何额外的列表或表格标记。
如何更改locations-0-city
以1 开头的数字,所以我希望locations-1-city
是默认值。以上是关于如何使用 FormFields 的 WTForms FieldList?的主要内容,如果未能解决你的问题,请参考以下文章
使用 formFields 导致 UnsupportedRequestContentTypeRejection 的 Akka Http 路由测试
使用 Flask-WTForms,如何设置 html 表单部分的样式?