8

我正在尝试生成一个动态复选框列表,其中根据数据状态检查了某些框。

这是我的表格:

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>

这会生成一个复选框列表,并且它可以工作,但我无法弄清楚如何指定要预先填充列表中的哪些复选框。

4

4 回答 4

5

我认为 jeverling 的答案非常接近,并导致我找到了一个经过测试的解决方案。我需要项目保持选中状态,但每次提供 url 时,复选框项目都会被清除,除非您可以指定选择。

重要的部分是 ChoiceObj(上面是 MyObj)从 object 继承,以便可以在其上调用 setattr。为了使这项工作,setattr(obj, attribute, value) 的参数在哪里

  • obj 是 ChoiceObj 实例
  • 属性是表单的名称
  • 选项列表中设置的值。

颜色.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>
于 2015-02-22T06:42:50.460 回答
5

使用FormFieldFieldList的组合:为了花费一点额外的样板和一些在持久时的手工绑定,您可以通过将提交分成多个字段来获得类似的结果。

这得益于作为WTForms的DRYer方法。如果你坚持它,你会发现你的表格工作得更顺利。这是因为您正在使用库中内置的默认行为。尽管该库允许您混合和匹配 Widget 类,但根据我的经验,有相当有限的组合子集可以很好地协同工作。如果你坚持基本的/组合并用/组合它们,事情会更好地工作。FieldValidatorFormFieldFieldList

请参见下面的示例:

代码

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()

原始 HTML

<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.dataPOST 后的结果

{'items': [{'item_id': 1, 'want': True}, {'item_id': 2, 'want': False}]}

于 2014-06-01T14:22:02.073 回答
2

我认为使用内部子类的方法应该可以解决这个问题:

http://wtforms.simplecodes.com/docs/1.0.1/specific_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)

请不要说这是未经测试的,但我认为它应该可以工作。
祝你好运!

于 2013-10-24T13:18:01.050 回答
1

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)
于 2021-02-25T21:55:45.717 回答