【问题标题】:How to pre-populate checkboxes with Flask/WTForms如何使用 Flask/WTForms 预填充复选框
【发布时间】:2013-11-03 01:13:36
【问题描述】:

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

这是我的表格:

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>

这会生成一个复选框列表,并且可以正常工作,但我不知道如何指定要预先填充列表中的哪些复选框。

【问题讨论】:

    标签: python flask wtforms


    【解决方案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)
    

    【讨论】:

    • 我希望您的回答对某人有所帮助。 Flask 很棒,但我至少有五年没用过了。
    【解决方案2】:

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

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

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

    color.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>
    

    【讨论】:

    • 我不会对此投票,因为我不再使用 Flask/WTForma。
    • 这是一段非常棒的代码。我使用了它的修改版本(删除了带有数据库查询的会话引用)。但是,我完全不知道它是如何工作的。能否给个解释或提供一些链接?
    • 这对 2019 年帮助太大了!
    • @EricWilson 请问,反对 Flask/WTF 的原因是什么?
    • @Geeocode 我不再从事我使用这个库的项目。
    【解决方案3】:

    使用 FormFieldFieldList 的组合:为了花费一点额外的样板和一些在持续时间的手工绑定,您可以通过中断提交来获得类似的结果进入多个领域。

    这得益于对 WTFormsDRYer 方法。如果你坚持它,你会发现你的表格工作得更顺利。这是因为您正在使用库中内置的默认行为。尽管该库允许您混合和匹配 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()
    

    原始 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>
    

    渲染结果

    POST 后form.data 的结果

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

    【讨论】:

    • 我在我的代码中使用了这种方法,但我无法从HiddenField 中提取数据。我的form.data 看起来像这样:{'removing_items': [{'pub_id': '&lt;input id="pub_id" name="pub_id" type="hidden" value="1"&gt;', 'remove': True}, {'pub_id': '&lt;input id="pub_id" name="pub_id" type="hidden" value="9"&gt;', 'remove': True}, {'pub_id': '&lt;input id="pub_id" name="pub_id" type="hidden" value="14"&gt;', 'remove': True}], 'submit': True}
    • 是的,正如有人在另一个答案中指出的那样 BooleanFieldSubmitField 不能在 FieldList 中正确使用。它们会正确渲染,但会在提交时失败。
    【解决方案4】:

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

    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)
    

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

    【讨论】:

      猜你喜欢
      • 2016-06-23
      • 2013-11-14
      • 1970-01-01
      • 2018-12-31
      • 1970-01-01
      • 1970-01-01
      • 2021-01-30
      • 1970-01-01
      • 2014-03-13
      相关资源
      最近更新 更多