【问题标题】:WTForm: FieldList with SelectField, how do I render?WTForms:带选择字段的字段列表,如何呈现?
【发布时间】:2014-08-09 09:57:41
【问题描述】:

我有一个允许我的用户创建订单的订单。一个订单由多个(producetype, quantity) 的元组组成。 Producetype 应该以<select> 形式呈现,而数量可以只是一个输入。应该动态添加生产类型的选择,因为这可能会改变。目前,我已经用纯 html 编写了这个

我想为此使用 WTForm,因为 WTForm 确实简化了我的代码。但是,我无法这样做:

代码:

class OrderEntryForm(Form):
  quantity = IntegerField('Quantity',
                          [validators.Required(), validators.NumberRange(min=1)])
  # we will be dynamically adding choices
  producetype = SelectField('Produce',
                            [validators.Required()],
                            choices=[])

class OrderForm(Form):
  name = TextField('Crop', [validators.Length(min=3, max=60)])
  due_date = DateField('Due Date', [validators.required()])
  order_entries = FieldList(FormField(OrderEntryForm))

我有以下问题:

  1. 如何向 OrderForm 的 order_entries 字段动态添加选项?
  2. 如果我有订单 order = { name:hello, due_date:2014-06-18, order_entries:[ {producetype_id: 1, quantity: 2}, {producetype_id: 3, quantity: 4}] },如何使用正确的 OrderEntryForm 值填充我的 OrderForm

【问题讨论】:

    标签: python flask wtforms flask-wtforms


    【解决方案1】:

    如何将选项动态添加到 OrderForm 的 order_entries 字段

    这取决于你的意思

    如果您的意思是要在渲染后向表单添加行项目。您必须使用 DOM 操作在客户端添加这些。 WTForms 具有用于寻址表单字段索引的命名约定。它只是name="<form-field-name>-<index>"。如果您使用遵循此约定的 javascript 添加名称,WTForms 将知道如何在后端处理它。

    如果你的意思是你想要一个动态选择列表,那么你可以在实例化后迭代表单中的FieldList。您可以分配 choices 任何可迭代的 2 元组。在下面的示例中,我直接分配它们,但它们可以很容易地从存储中检索。

    order_form = OrderForm()
    for sub_form in order_form.order_entries:
        sub_form.producetype.choices = [('2', 'apples'), ('2', 'oranges')]
    

    如何使用正确的 OrderEntryForm 值填充我的 OrderForm?

    您可以使用obj 关键字参数直接将对象绑定到表单。 WTForms 足够聪明,可以根据对象的属性动态构建表单。

    from wtforms import Form, IntegerField, SelectField, TextField, FieldList, FormField
    from wtforms import validators
    from collections import namedtuple
    
    OrderEntry = namedtuple('OrderEntry', ['quantity', 'producetype'])
    Order = namedtuple('Order', ['name', 'order_entries'])
    
    class OrderEntryForm(Form):
      quantity = IntegerField('Quantity',
                              [validators.Required(), validators.NumberRange(min=1)])
      # we will be dynamically adding choices
      producetype = SelectField('Produce',
                                [validators.Required()],
                                choices=[
                                    (1, 'carrots'),
                                    (2, 'turnips'),
                                ])
    
    class OrderForm(Form):
      name = TextField('Crop', [validators.Length(min=3, max=60)])
      order_entries = FieldList(FormField(OrderEntryForm))
    
    # Test Print of just the OrderEntryForm
    o_form = OrderEntryForm()
    print o_form.producetype()
    
    # Create a test order
    order_entry_1 = OrderEntry(4, 1)
    order_entry_2 = OrderEntry(2, 2)
    
    order = Order('My First Order', [order_entry_1, order_entry_2])
    
    order_form = OrderForm(obj=order)
    
    print order_form.name
    print order_form.order_entries
    

    上面的示例创建了一个示例Order 并将其提供给obj 关键字。在渲染时,这将生成以下(无样式):

    【讨论】:

    • 这很有帮助。谢谢你。但是,我还有另一个与您的解决方案直接相关的问题。你也好心看看吗? stackoverflow.com/questions/24324161/…
    • 你如何设计这个以摆脱 Order Entries-0 等?
    • 您可以在To render a field, simply call it, providing any values the widget expects as keyword arguments. Usually the keyword arguments are used for extra HTML attributes. 内添加样式,或者您可以在文档本身中添加样式表,就像使用 html/css 时一样。
    【解决方案2】:

    对于其他被SelectFields + FieldLists 难倒的人,这就是我用SelectFields 实现FieldList 的方式(灵感来自nsfyn55's answer)。这种方法根据 JSON 数据动态地将任意数量的 SelectField 渲染到 /home 路由。

    路线(routes.py):

    @app.route('/home', methods=['POST', 'GET'])
    def home():
        custom_metadata = data
    
        select_metadata_form_list = SelectFormList()
        select_metadata_form_list.select_entries = get_select_entries()
    
        context = {
            "select_metadata_form_list": select_metadata_form_list,
        }
    
        return render_template('home.html', **context)
    

    表格(forms.py):

    class SelectForm(FlaskForm):
        select = SelectField("Placeholder", choices=[])
    
    
    class SelectFormList(FlaskForm):
        select_entries = FieldList(FormField(SelectForm))
    

    模板 (home.html):

    {% for select_form in select_metadata_form_list.select_entries %}
        {{select_form.select.label}}: {{ select_form.select}}
    {% endfor %}
    

    辅助方法 (app.py):

    def get_select_entries():
        """
        Converts custom metadata to a forms.SelectForm(), which can then be
        used by SelectFormlist() to dynamically render select items.
    
        :return: <forms.SelectForm object>
        """
        select_data = get_select_data_from_custom_metadata()
    
        select_data_labeled = get_labled_select_data(select_data=select_data)
    
        all_select_items = []
        for select_dict in select_data_labeled:
            for k, v in select_dict.items():
                select_id = uuid.uuid1()   # allows for multiple selects
                select_entry = SelectForm()
                select_entry.select.label = k
                select_entry.id = select_id
                select_entry.select.choices = v
                all_select_items.append(select_entry)
    
        return all_select_items
    
    def get_select_data_from_custom_metadata():
        """
        [
        {"Seniority": ["Intern", "Associate", "Senior"]}
        ]
        :return: List of dictionaries containing key and list of select values
        """
        type = "select"
        select_data = []
        custom_metadata = get_custom_metadata()
    
        for field in custom_metadata["fields"]:
            if field["type"] == type:
                select_data.append({field["key"]: field["data_list"]})
    
        return select_data
    

    “数据” (custom_metadata.json):

    {
      "fields": [
        {
          "type": "text",
          "key": "Position"
        },
    
        {
          "type": "select",
          "key": "Seniority",
          "data_list": ["Intern", "Associate", "Senior", "Executive"]
        },
        {
          "type": "select",
          "key": "Company",
          "data_list": ["Ford", "Chevy", "Toyota"]
        },
        {
          "type": "select",
          "key": "Team",
          "data_list": ["Bucks", "Raptors", "Thunder"]
        }
      ]
    }
    

    结果

    【讨论】:

      【解决方案3】:

      SubmitField 添加到您的OrderForm

      submit_something = SubmitField((u'Add something'))
      

      然后从你的视图中调用它,并使用FieldListappend_entry方法:

      if form.submit_something.data:
               form.order_entries.append_entry()
               return render_template('yourtemplate.html', form=form)
      

      希望有帮助!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-13
        • 2015-02-02
        • 1970-01-01
        • 2012-10-07
        • 2011-08-25
        • 1970-01-01
        • 2016-07-30
        相关资源
        最近更新 更多