烧瓶 + WTForms,动态生成的字段列表



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

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

因此,有问题的表单允许用户提交包含以下内容的对象:

  • 标签(字符串字段,简单)
  • 描述(TextAreaField,也很容易;尽管我很难使默认值起作用)
  • 形式(谓词,对象
  • )的属性列表,其中谓词取自预构建的列表,对象基本上可以是任何东西,但每个谓词将生成一个特定的对象(例如,谓词"相关"将期望另一个对象(来自下拉列表),谓词"资源"将期望某种http链接)。此列表可以为空。

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

下面是窗体的视图,使用一些属性初始化:

https://i.stack.imgur.com/g7g40.png

在"专有"标签下,您有一个用于选择谓词的下拉列表,根据所选谓词(可以是下拉列表或文本字段)显示或隐藏第二个字段,并且只有当您单击"Ajouter propriété"("添加属性")时,才会在下面的选项卡中添加新行并生成字段。

我不想在这方面更改任何内容,因为它运行良好,使表单非常直观,并且基本上正是我从用户端想要的。

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

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 实例获取标签和说明工作正常,但属性始终返回空列表。理想情况下,我希望能够在调用 cat_form.properties.data 时获得表单列表 [(谓词 1, 属性 1), (谓词 2, 对象 2) ... ] (这就是为什么我有一个 FieldList of FormFields,每个字段有两个 HiddenField),但是只要它使用 WTForms,我就必须从两个列表中构建这样的列表。知道吗?非常感谢:)

我通过使用 FieldList 对象和 append_entry() 来找出问题所在,看看如果我制作一个预填充的属性列表,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生成的每个子表单中。

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

这是了解其工作原理的好地方。当然,你还有另一个选择何时隐藏或显示相关字段的问题,但这也可以由javascript/jQuery脚本使用jQuery的.change()事件来处理。像这样:

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

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

最新更新