Flask + WTForms,动态生成字段列表
Flask + WTForms, dynamically generated list of fields
我正在制作一个本质上基于表单的 Flask 应用程序,因此我正在使用 WTForms 和 Flask-wtf。
我目前正在重构我的代码,所以我的整个表单都使用 WTForms,其中一个表单有一个非常动态的部分,我无法使用 WTForms 实现。我不知道该怎么做,我最初的想法没有奏效,我找不到涵盖我的问题的参考资料或教程,所以这就是我寻求帮助的原因。
因此,所讨论的表单允许用户提交包含以下内容的对象:
- 一个标签(StringField,简单)
- 描述(TextAreaField,也很简单;虽然我很难使默认值起作用)
- 形式的属性列表(谓词,对象),其中谓词取自预先构建的列表,对象基本上可以是任何东西,但每个谓词都会生成一个特定的对象(例如,谓词"related to" 将期待另一个对象(来自下拉列表),谓词 "resource" 将期待某种 http link)。此列表可以为空。
如您所料,我对列表有疑问。代码现在的工作方式,我使用 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 生成的每个子表单中。
我正在制作一个本质上基于表单的 Flask 应用程序,因此我正在使用 WTForms 和 Flask-wtf。
我目前正在重构我的代码,所以我的整个表单都使用 WTForms,其中一个表单有一个非常动态的部分,我无法使用 WTForms 实现。我不知道该怎么做,我最初的想法没有奏效,我找不到涵盖我的问题的参考资料或教程,所以这就是我寻求帮助的原因。
因此,所讨论的表单允许用户提交包含以下内容的对象:
- 一个标签(StringField,简单)
- 描述(TextAreaField,也很简单;虽然我很难使默认值起作用)
- 形式的属性列表(谓词,对象),其中谓词取自预先构建的列表,对象基本上可以是任何东西,但每个谓词都会生成一个特定的对象(例如,谓词"related to" 将期待另一个对象(来自下拉列表),谓词 "resource" 将期待某种 http link)。此列表可以为空。
如您所料,我对列表有疑问。代码现在的工作方式,我使用 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 生成的每个子表单中。