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

4
我正在制作一个基于表单的Flask应用程序,因此我正在使用WTForms和Flask-wtf。
目前,我正在重构代码,使我的整个表单都使用WTForms。但是,在其中一个表单中有一个非常动态的部分,我无法使用WTForms实现它。我不知道该怎么做,我的初始想法没有起作用,我找不到涵盖我的问题的参考或教程,这就是为什么我寻求帮助的原因。
因此,所讨论的表单允许用户提交由以下内容组成的对象:
- 标签(StringField,容易) - 描述(TextAreaField,也很容易;虽然我在使默认值生效方面遇到了麻烦) - 表单属性列表(谓词、对象),其中谓词来自预先构建的列表,而对象可以是任何东西,但每个谓词都将生成特定的对象(例如,谓词“相关”将期望另一个对象(来自下拉列表),而谓词“资源”将期望某种类型的http链接)。该列表可以为空。
作为你们可以猜到的,我在列表方面遇到了麻烦。目前代码的工作方式是,我使用wtforms获取标签和描述,使用配置常量(在整个代码中都使用)和javascript中的动态菜单生成属性列表(这里是谓词),然后我可以在视图函数中使用flask.request.form对象获取这些字段。所有谓词的隐藏字段具有相同的name属性,所有对象的隐藏字段也具有相同的name属性。
这是表单视图的样子,初始化了一些属性:

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

在“Propriétés”标签下有一个下拉菜单,可选择谓词,第二个字段根据所选谓词的不同显示或隐藏(可以是下拉菜单或文本字段),只有当您点击“添加属性”时才会在下面的选项卡中添加新行并生成该字段。
我希望不需要在此方面进行任何更改,因为它运行良好,使表单非常直观,并且基本上完全符合我从用户端想要的内容。
这就是我的自定义表单现在的样子(它不起作用,无论我提交多少个字段,属性都为空):
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时获得形式为[(predicate1, property1), (predicate2, object2) ...]的列表(这就是为什么我有一个每个HiddenField中有两个FormField的FieldList),但如果使用WTForms来构建这样的列表,我不会有任何问题。有什么建议吗?非常感谢 :)
2个回答

3
我通过操作FieldList对象和append_entry()方法来发现了问题,以查看如果我创建一个预填充的属性列表,Flask-wtf会生成什么HTML代码。
我的Javascript正在生成具有相同名称的隐藏字段,因为据我所知,WTForms可以聚合具有相同名称的字段来创建列表。问题是,这些具有类似名称的字段本身是嵌套在名为properties的FieldList对象中的FormField中。
为了让WTForms表单对象区分一组隐藏字段与另一组隐藏字段,当您将FormField嵌套在FieldList中时,它会使用"FieldList_name-index-"前缀为FormField字段命名。这意味着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生成的隐藏字段数据,这就是问题所在。

编辑:表单验证发生的原因是您必须为使用FormField生成的每个子表单插入具有您的csrf的CSRF隐藏输入。


我遇到了完全相同的问题,想知道我的数据丢失在哪里。一开始感觉很有道理,但非常不直观。 - kev

0
使用jQuery来处理表单中的更具动态性的元素/行为。请注意,表单字段具有隐藏属性(或方法,具体取决于您是否使用bootstrap),使您能够呈现可能需要的所有内容,但仅在必要时显示字段,并在其他情况下隐藏它们。动态添加字段有点困难,但不是不可能的。与属性关联的字段数量是否有限制?如果有的话,我只会呈现最大数量的字段(只要合理,最多可达5个,当用户可以添加的属性的最大数量达到两位数时,呈现一堆永远不会使用的字段就变得不优雅了)。
这里一个很好的地方可以看到它是如何工作的。当然,您还有另一个问题,即选择何时隐藏或显示相关字段,但是这也可以通过javascript / jQuery脚本来处理,使用jQuery的.change()事件。类似这样:
$("#dropdown").change(function () {
    var chosen_val = $(this).val();
    if (chosen_val == 'banana'){$('#property1').show();} else {$('#property1').hide();}
   });

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


可以创建任意数量的属性。基本上我正在使用输入数据创建rdf图形。因此,我的表单的默认状态是在开始时没有隐藏字段。当用户与表单交互时,我会动态创建隐藏字段。问题是我希望这些动态创建的隐藏字段可以被WTForms表单类访问,但我不知道该怎么做。我没有显示和隐藏属性字段的问题,问题是在服务器端,我想使用WTForms访问它们的数据。 - Elred
我会看看 Flask-Bootstrap,因为我确实在使用 Bootstrap(但仅用于 css),不过我感觉你可能要么没理解我的问题,要么是我没理解你的意思。 - Elred
所以,只是为了澄清一下,不是列表的结构或表单的显示引起了我的问题(我已经处理好了这部分的所有内容,正如我所说,我正在重构,因此我有一个可工作的应用程序),只是我想使用WTForms来处理整个表单,而不是从request.form对象获取数据并从CategoryForm对象获取所有内容,我不知道正确的方法。 - Elred

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接