我正在尝试生成一个动态复选框列表,其中根据数据的状态选中了某些框。
这是我的表格:
class FooForm(Form):
bar = SelectMultipleField(
'Bar',
option_widget=CheckboxInput(),
widget=ListWidget(prefix_label=True))
这是控制器:
@app.route('/fooform', methods = ['GET','POST'])
def foo():
foos = foo_dao.find()
form = FooForm()
form.bar.choices = [(foo.id, foo.label) for foo in foos]
# SOMEHOW PRE-POPULATE CHECKBOXES HERE
if form.is_submitted():
# DO STUFF
return render_template('foo.html',
foos=foos,
form=form)
这是模板:
<form action="" method="post" name="foos">
{{form.bar}}
<p><input type="submit" value="Add"></p>
</form>
这会生成一个复选框列表,并且它可以工作,但我不知道如何指定要预先填充列表中的哪些复选框。
我认为 jeverling 的答案非常接近,并引导我找到了经过测试的解决方案。 我需要保持选中的项目,但每次提供 url 时,复选框项目都会被清除,除非您可以指定选择。
重要的部分是ChoiceObj(上面是MyObj)继承自object,以便可以在其上调用setattr。为了实现这一点,setattr(obj, attribute, value) 的参数在哪里
颜色.py:
from flask.ext.wtf import Form
from flask import Flask, render_template, session, redirect, url_for
from wtforms import SelectMultipleField, SubmitField, widgets
SECRET_KEY = 'development'
app = Flask(__name__)
app.config.from_object(__name__)
class ChoiceObj(object):
def __init__(self, name, choices):
# this is needed so that BaseForm.process will accept the object for the named form,
# and eventually it will end up in SelectMultipleField.process_data and get assigned
# to .data
setattr(self, name, choices)
class MultiCheckboxField(SelectMultipleField):
widget = widgets.TableWidget()
option_widget = widgets.CheckboxInput()
# uncomment to see how the process call passes through this object
# def process_data(self, value):
# return super(MultiCheckboxField, self).process_data(value)
class ColorLookupForm(Form):
submit = SubmitField('Save')
colors = MultiCheckboxField(None)
allColors = ( 'red', 'pink', 'blue', 'green', 'yellow', 'purple' )
@app.route('/', methods=['GET', 'POST'])
def color():
selectedChoices = ChoiceObj('colors', session.get('selected') )
form = ColorLookupForm(obj=selectedChoices)
form.colors.choices = [(c, c) for c in allColors]
if form.validate_on_submit():
session['selected'] = form.colors.data
return redirect(url_for('.color'))
else:
print form.errors
return render_template('color.html',
form=form,
selected=session.get('selected'))
if __name__ == '__main__':
app.run()
和模板/color.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form method="post">
<table>
<tr>
<td>
{{ form.hidden_tag() }}
{{ form.colors }}
</td>
<td width="20"></td>
<td>
<b>Selected</b><br>
{% for s in selected %}
{{ s }}<br>
{% endfor %}
</td>
</tr>
</table>
<input type="submit">
</form>
</body>
</html>
使用
FormField
和FieldList
的组合:为了花费一点额外的样板和持久时间的一些手工绑定,您可以通过将提交分成多个字段来获得类似的结果。
这受益于WTForms的DRYer方法。如果你坚持下去,你会发现你的表格工作得更加顺利。这是因为您正在使用库中内置的默认行为。尽管该库允许您混合和匹配 Widget 类,但根据我的经验,可以很好地协同工作的组合子集相当有限。如果您坚持使用基本的
Field
/Validator
组合并使用 FormField
/FieldList
进行组合,效果会更好。
请参阅下面的示例:
from collections import namedtuple
from wtforms import Form, FieldList, BooleanField, HiddenField, FormField
from webob.multidict import MultiDict
GroceryItem = namedtuple('GroceryItem', ['item_id', 'want', 'name'])
class GroceryItemForm(Form):
item_id = HiddenField()
want = BooleanField()
class GroceryListForm(Form):
def __init__(self, *args, **kwargs):
super(GroceryListForm, self).__init__(*args, **kwargs)
# just a little trickery to get custom labels
# on the list's checkboxes
for item_form in self.items:
for item in kwargs['data']['items']:
if item.item_id == item_form.item_id.data:
item_form.want.label =''
item_form.label = item.name
items = FieldList(FormField(GroceryItemForm))
item1 = GroceryItem(1, True, 'carrots')
item2 = GroceryItem(2, False, 'cornmeal')
data = {'items': [item1, item2]}
form = GroceryListForm(data=MultiDict(data))
print form.items()
<ul id="items">
<li>
carrots
<table id="items-0">
<tr>
<th></th>
<td><input id="items-0-item_id
" name="items-0-item_id" type="hidden" value="1"><input checked id="items-0-want" name="it
ems-0-want" type="checkbox" value="y"></td>
</tr>
</table>
</li>
<li>
cornmeal
<table id="items
-1">
<tr>
<th></th>
<td><input id="items-1-item_id" name="items-1-item_id" type="hidden" valu
e="2"><input id="items-1-want" name="items-1-want" type="checkbox" value="y"></td>
</tr>
</t
able>
</li>
</ul>
form.data
的结果{'items': [{'item_id': 1, 'want': True}, {'item_id': 2, 'want': False}]}
我认为使用内部子类的方法应该可以解决这个问题:
http://wtforms.simplecodes.com/docs/1.0.1/special_problems.html#dynamic-form-composition
您必须在初始化表单之前设置选择,您可以使用内部子类来完成此操作。 然后您可以将一个对象传递给表单,wtforms 将使用该对象来预填充您的字段:
def foo():
class F(FooForm):
pass
choices = [(foo.id, foo.label) for foo in foos]
F.bar.choices = choices
class MyObj(object):
pass
obj = MyObj()
for choice in choices:
setattr(obj, choice, True)
form = F(request.POST or None, obj=obj)
请注意,这尚未经过测试,但我认为它应该有效。
祝你好运!
WTForms 的 SelectMultipleField 通过将每个项目的值(示例中的 foo.id)与字段的 .data 列表进行比较来确定是否选中某个框。 (检查 WTForms 源代码以验证这一点。)
我就这样解决了这个问题。我的目标是预先检查每个框,因此我包含了该选项,并注释掉了:
@app.route('/fooform', methods = ['GET','POST'])
def foo():
foos = foo_dao.find()
form = FooForm()
form.bar.choices = [(foo.id, foo.label) for foo in foos]
if form.is_submitted():
for x in form.bar.data:
#ACT ON EACH USER-SELECTED ITEM HERE
else: #precheck the boxes here
form.bar.data = [1,2,5,8] #id's of items to be prechecked on form
#form.bar.data = [x[0] for x in form.bar.choices] #this prechecks every box
return render_template('foo.html',
foos=foos,
form=form)
这里的答案是适用于我的案例的想法:https://stackoverflow.com/a/29907194/2816571
关键是,与某些 html 元素不同,
form.field(value="value")
construct 用于动态设置值,键/值参数“checked”必须在 template.html 中使用
{{form.booleanField(checked=True)}}
{{form.booleanField(checked=False)}}
或者更一般地说,如果 is_admin 是传递给模板的参数:
{{form.booleanField(checked=is_admin)}}