【问题标题】:Flask + WTForms, dynamically generated list of fieldsFlask + WTForms,动态生成的字段列表
【发布时间】:2015-03-05 14:56:09
【问题描述】:

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

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

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

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

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

下面是表单视图的样子,用一些属性初始化:

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

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

【问题讨论】:

    标签: python dynamic flask wtforms


    【解决方案1】:

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

    【讨论】:

    • 我遇到了完全相同的问题,想知道我的数据在哪里丢失了。有道理,但一开始很不直观。
    【解决方案2】:

    在表单中使用 jQuery 来获得更多动态元素/行为。请注意,表单字段具有隐藏属性(或方法,例如,如果您使用引导程序),允许您呈现您可能需要的所有内容,但仅在需要时显示字段,否则隐藏它们。动态添加字段有点困难,但并非不可能。与属性关联的字段数量是否有限制?如果是,我将只呈现最大数量的字段(只要合理,最多 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”字段始终存在,如果用户选择正确的下拉值,则等待显示。

    【讨论】:

    • 可以创建的属性数量没有限制。基本上我正在做的是使用输入数据创建一个 rdf 图。所以我的表单的默认状态是,当你开始时,里面根本没有隐藏字段。当用户与表单交互时,我会动态创建隐藏字段。问题是我希望 WTForms Form 类可以访问那些动态创建的隐藏字段,但我不知道该怎么做。我在显示和隐藏属性字段方面没有问题,问题是服务器端我想使用 WTForms 访问他们的数据。
    • 我会看看 Flask-Bootstrap,因为我确实在使用引导程序(但仅用于 css),但我有一种感觉,我要么不明白你的观点,要么你误解了我的问题。
    • 所以澄清一下,不是列表的结构也不是表单的显示导致我出现问题(我已经为这部分处理了所有事情,正如我所说我正在重构,所以我有一个工作应用程序),只是我想使用 WTForms 处理整个表单,而不必从 request.form 对象获取数据并从 CategoryForm 对象获取所有内容,而且我不知道正确的方法这个。
    猜你喜欢
    • 2012-10-02
    • 1970-01-01
    • 2020-05-02
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 2019-07-23
    • 2013-08-13
    • 1970-01-01
    相关资源
    最近更新 更多