Flask + WTForms,动态生成字段列表

Flask + WTForms, dynamically generated list of fields

我正在制作一个本质上基于表单的 Flask 应用程序,因此我正在使用 WTForms 和 Flask-wtf。

我目前正在重构我的代码,所以我的整个表单都使用 WTForms,其中一个表单有一个非常动态的部分,我无法使用 WTForms 实现。我不知道该怎么做,我最初的想法没有奏效,我找不到涵盖我的问题的参考资料或教程,所以这就是我寻求帮助的原因。

因此,所讨论的表单允许用户提交包含以下内容的对象:

如您所料,我对列表有疑问。代码现在的工作方式,我使用 wtforms 获取标签和描述,属性列表是使用配置常量生成的(在整个代码中使用,所以如果我想添加新属性,我只有一个地方可以编辑) javascript 中的动态菜单创建(此处为谓词)字段,然后我可以在视图函数中使用 flask.request.form 对象。谓词的所有隐藏字段具有相同的名称属性,对象的所有隐藏字段具有相同的名称属性。

这是表单视图的样子,使用一些属性进行了初始化:

http://i.imgur.com/bfMG95s.png

在 "Propriétés" 标签下,您有一个指向 select 谓词的下拉菜单,第二个字段显示或隐藏取决于 selected 谓词(可以是下拉菜单或文本字段),只有当您单击 "Ajouter propriété" ("Add property") 时,才会在下面的选项卡中添加新行并生成字段。

我不想在这方面进行任何更改,因为它工作得很好,使表单非常直观,基本上正是我希望用户端看到的样子。

这就是我的自定义表单现在的样子(它不起作用,无论我用表单提交多少字段,属性都保持为空):

class PropertyForm(Form):
    property_predicate = HiddenField(
        validators=[AnyOf(values=app.config["PROPERTY_LIST"].keys())]
    )
    property_object = HiddenField(
        validators=[DataRequired()]
    )

class CategoryForm(Form):
    """
        Custom form class for creating a category with the absolute minimal
        attributes (label and description)
    """
    label = StringField(
        "Nom de la categorie (obligatoire)",
        validators=[DataRequired()]
    )
    description = TextAreaField(
        "Description de la categorie (obligatoire)",
        validators=[DataRequired()]
    )
    properties = FieldList(FormField(PropertyForm),validators=[Optional()])

下面是我想在我的 views.py 代码(我目前正在重构)中做的事情:

def cat_editor():
    cat_form = CategoryForm()
    if request.method == "GET":
        # Do GET stuff and display the form
        return render_template("cateditor.html", form=cat_form, varlist=template_var_list)
    else if request.method == "POST":
        if cat_form.validate_on_submit():
            # Get values from form
            category_label = cat_form.label.data
            category_description = cat_form.description.data
            category_properties = cat_form.properties.data
            # Do POST stuff and compute things
            return redirect(url_for("index"))
        else:
            # form didn't validate so we return the form so the template can display the errors
            return render_template("cateditor.html", form=cat_form,
                                    template_var_list = template_var_list)

基本结构完美无缺,只是那个该死的动态列表我无法正常工作。

从 WTForms CategoryForm 实例获取标签和描述工作正常,但属性总是 return 一个空列表。理想情况下,我希望能够在调用 cat_form.properties.data 时获得 [(predicate1, property1), (predicate2, object2) ... ] 形式的列表(这就是为什么我有一个带有两个 FormFields 的 FieldList HiddenField 在每个),但只要它使用 WTForms,我就不必从两个列表构建这样的列表。任何想法?非常感谢:)

使用 jQuery 以获得表单中更动态的元素/行为。请注意,表单字段有一个隐藏的 属性(或方法,取决于例如,如果您使用 bootstrap),允许您呈现您可能需要的所有内容,但仅在需要时显示字段,并且以其他方式隐藏它们。动态添加字段有点难,但并非不可能。与属性关联的字段数量是否有限制?如果是,我只渲染最大数量的字段(只要合理,最多 5 个似乎没问题,当你达到两位数作为用户可以添加的最大属性数量时,渲染一堆字段你'永远不会使用 gets 是不优雅的)。

Here's 一个了解它如何运作的好地方。当然,您还有另一个选择何时隐藏或显示相关字段的问题,但这也可以通过 javascript/jQuery 脚本使用 jQuery 的 .change() 事件来处理。像这样:

$("#dropdown").change(function () {
    var chosen_val = $(this).val();
    if (chosen_val == 'banana'){$('#property1').show();} else {$('#property1').hide();}
   });

此代码可能 不会 工作,并且肯定缺乏正确的逻辑,但应该让您了解如何使用 jQuery 解决此问题。请注意,'property1' 字段始终存在,等待在用户选择正确的下拉值时显示。

我通过使用 FieldList 对象和 append_entry() 来了解问题所在,看看如果我要制作一个预填充的 [=31],Flask-wtf 会生成什么 HTML 代码=] 列表.

我的 Javascript 正在生成具有相同名称的隐藏字段,据我了解,WTForms 能够聚合具有相同名称的字段以创建列表。问题是,那些类似命名的字段是嵌套在 FieldList 对象名称属性中的 FormField 本身的一部分。

为了让 WTForms Form 对象区分一组隐藏字段和另一组隐藏字段,当您将 FormFields 嵌套在 FieldList 中时,它会在 FormFields 字段名称前加上前缀 "FieldList_name-index-"。这意味着 WTForms 所期望的是

<input type="hidden", name="properties-0-property_predicate" value=...>
<input type="hidden", name="properties-0-property_object" value=...>

<input type="hidden", name="properties-1-property_predicate" value=...>
<input type="hidden", name="properties-1-property_object" value=...>

<input type="hidden", name="properties-2-property_predicate" value=...>
<input type="hidden", name="properties-2-property_object" value=...>

我修改了 javascript 以便它生成适当的名称。现在,当我调用 cat_form.properties.data 时,我得到的结果如下所示:

[{"property_predicate": "comment", "property_object":"bleh"},
 {"property_predicate": "comment", "property_object": "bleh2"}]

这正是我所需要的。出于某种原因,表单无法验证,但至少我知道如何让 WTForms 提取我的 javascript 生成的隐藏字段的数据,这就是问题所在。

编辑:进行表单验证是因为您必须将带有 csrf 的 CSRF 隐藏输入插入到您使用 FormField 生成的每个子表单中。