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

时间:2015-01-07 16:35:24

标签: python dynamic flask wtforms

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

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

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

  • 标签(StringField,easy)
  • 描述(TextAreaField,也很简单;虽然我无法使默认值有效)
  • 表单属性列表(谓词,对象),其中谓词取自预建列表,对象基本上可以是任何内容,但每个谓词都会生成一个特定对象(例如,谓词“相关”)将期望另一个对象(来自下拉列表)和谓词“资源”将期望某种类型的http链接。此列表可以为空。

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

以下是表单视图的外观,初始化为一些属性:

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

在“Propriétés”标签下,您有一个下拉列表来选择谓词,第二个字段会根据所选谓词显示或隐藏(可以是下拉列表或文本字段),只有当您点击“ Ajouterproprié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时获得表单[(predicate1,property1),(predicate2,object2)......]的列表(这就是为什么我有一个FormFields的FieldList有两个每个中都有HiddenField,但只要它使用WTForms,我就可以从两个列表中构建这样的列表。任何的想法?非常感谢:)

2 个答案:

答案 0 :(得分:2)

我通过使用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生成的每个子表单中。

答案 1 :(得分:0)

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

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

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

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