我正在尝试在 Flask 中设置一个动态表单,其中该表单具有动态填充的 FormFields FieldList。虽然大部分都有效,但我不断遇到每个 FormField 的变量问题。我需要每个 FormField 有一个标签,它是 FormField 的名称(并在页面上呈现)和一个在显示/隐藏脚本中使用的自定义 ID,该脚本使用动态填充的 SelectField 来选择要显示和显示的子表单隐藏。但是,我无法让变量在提交表单后保留其状态,这也使我无法正确处理返回的数据。
这些是我的表单类:
class ColumnForm(Form):
text = Label('', '')
condition = SelectField('Condition', choices=[('eq', 'Equal to'), ('contains', 'Contains')], validators=[Optional()])
value = StringField(validators=[Optional()])
class CategoryForm(Form):
catName = ''
Columns = FieldList(FormField(ColumnForm))
def add_field(self, name):
# I have tried several ways of adding the formFields to the FieldList, this is the only way I get the variables to be renamed, but I do not have a way to get the submitted data back
if not self.checkIfformInFieldList(name):
self.Columns.append_entry()#{'text' : name})
self.Columns[-1].form.text = Label(len(self.Columns)-1, name)
self.Columns[-1].name = name
self.Columns[-1].id = name
self.Columns[-1].form.condition.id = f'{name}-condition'
self.Columns[-1].form.condition.name = f'{name}-condition'
self.Columns[-1].form.value.name = name+'-value'
self.Columns[-1].form.value.id = name+'-value'
self.Columns[-1].form.value.validators = [Optional()]
def checkIfformInFieldList(self, name)->bool:
for column in self.Columns:
if column.name == name:
return True
return False
class baseForm(FlaskForm):
table_select = SelectField('Select Table', choices=['Select Table'],validators=[Optional()])
submit = SubmitField('Filter')
tables = FieldList(FormField(CategoryForm))
def add_category(self, catName:str, columnNames:list):
self.tables.append_entry()
self.tables[-1].name = catName
self.tables[-1].id = catName
for columnName in columnNames:
self.tables[-1].add_field(columnName)
这是我的表单(的简化版本)初始化:
@app.route('/', methods=['GET', 'POST']))
def index():
form = baseForm(request.form)
databaseStructure = get_database_structure(db_dict)
for table in databaseStructure:
form.table_select.choices.append(table)
if not form.tables.entries:
for table, columns in databaseStructure.items():
form.add_category(table, columns)
if request.method == 'POST' and form.validate():
print(form.data)
return render_template('index.html', form = form)
这是我的 html 模板的(简化版本):
<form method="post">
{{ form.hidden_tag() }}
<p>
{{ form.table_select.label }}<br>
{{ form.table_select }}
</p>
{% for category in form.tables %}
<div id = "{{ category.name }}">
{% for column in category.Columns %}
<p>
{{ column.form.text }}
{{ column.form.condition }}
{{ column.form.value }}
</p>{% endfor %}
</div>
{% endfor %}
<p>{{ form.submit() }}</p>
</form>
<script>
document.addEventListener('DOMContentLoaded', function () {
const tableSelect = document.querySelector('#table_select');
const columnSelect = document.querySelector('#column_select');
tableSelect.addEventListener('change', function () {
localStorage.setItem('selectedTable', tableSelect.value)
});
});
window.onload = showFormFunction()
document.getElementById("table_select").onchange = function() {showFormFunction()};
function showFormFunction() {
let table_select_value = document.getElementById("table_select").value;
{% for tableName in form.tables %}{% if loop.index0 == 0 %}
if (table_select_value == "{{ tableName.id }}"){ {% else %} else if (table_select_value == "{{ tableName.id }}"){ {% endif %}
document.getElementById("{{ tableName.id }}").style.display = "inline"; {% for tableNamex in form.tables %} {% if tableName.id != tableNamex.id %}
document.getElementById("{{ tableNamex.id }}").style.display = "none"; {% endif %} {% endfor %}
}{% endfor %} else { {% for tableName in form.tables %}
document.getElementById("{{ tableName.id }}").style.display = "none";{% endfor %}
}
};
</script>
遇到的问题是,在每次请求时都会重新创建 Flask-WTF 表单及其字段,这意味着在
add_field
方法中设置的任何自定义属性都会在下次服务器收到请求时丢失。
为了解决这个问题,可以使用以下方法之一:
1. 使用隐藏字段存储自定义变量:
可以为每个
ColumnForm
实例添加隐藏字段,以存储
name
和
id
值。然后,可以在呈现表单和处理提交的数据时访问这些隐藏字段。
```python class ColumnForm(Form): text = Label('', '') condition = SelectField('Condition', choices=[('eq', 'Equal to'), ('contains', 'Contains')], validators=[Optional()]) value = StringField(validators=[Optional()]) # 添加隐藏字段来存储自定义变量 name = HiddenField() id = HiddenField()
# 在 add_field 方法中设置隐藏字段的值 self.Columns[-1].form.name.data = name self.Columns[-1].form.id.data = name ```
在 HTML 模板中,需要渲染隐藏字段:
```html
{% for column in category.Columns %}
{{ column.form.text }} {{ column.form.condition }} {{ column.form.value }} {{ column.form.name }} {{ column.form.id }}
{% endfor %} ```
在处理表单提交时,可以从隐藏字段中访问
name
和
id
值。
2. 使用动态创建字段的名称:
可以在创建字段时使用动态生成的名称,并在处理表单提交时使用这些名称来访问相应的值。
```python class CategoryForm(Form): # ...
def add_field(self, name):
# ...
# 使用动态生成的名称创建字段
self.Columns.append_entry({
'text': Label(len(self.Columns)-1, name),
'condition': SelectField('Condition', choices=[('eq', 'Equal to'), ('contains', 'Contains')], validators=[Optional()], name=f'{name}-condition', id=f'{name}-condition'),
'value': StringField(validators=[Optional()], name=f'{name}-value', id=f'{name}-value')
})
# 在处理表单提交时,使用动态生成的名称访问字段的值 for category in form.tables: for column_name in category.Columns.data: condition_value = category.data[f'{column_name}-condition'] value = category.data[f'{column_name}-value'] ```
这种方法不需要隐藏字段,但代码可能更难阅读和维护。
3. 使用 JavaScript 处理表单数据:
可以使用 JavaScript 在客户端处理表单数据,并在提交表单之前添加必要的自定义变量。
javascript
// 在提交表单之前修改表单数据
const form = document.querySelector('form');
form.addEventListener('submit', (event) => {
// ... 添加自定义变量到表单数据
form.submit();
});
这种方法可以使的 Python 代码更简洁,但需要更多 JavaScript 代码。
选择最适合的方法取决于的具体需求和编码风格。
标签:python,flask,flask-wtforms From: 78461420