表单不验证。 csrf_token 包括在内
Form does not validate. csrf_token included
我的 Flask 应用程序中的 validate_on_submit() 方法存在以下问题。
快速概览我有什么:
我的表单 class 是根据输入动态创建的:
class EditUsers(FlaskForm):
submit = SubmitField('Submit')
def form_builder(roles_list):
class EditUsersForm(EditUsers):
pass
for role in roles_list:
if isinstance(role, tuple) and len(role) == 5:
if not role[2] and role[3]:
setattr(EditUsersForm, f'{role[0]}_{role[4]}_bool', BooleanField(label=role[0]))
setattr(EditUsersForm, f'{role[0]}_{role[4]}_date', SelectField('Expiration Period', choices=choices))
else:
raise IncorrectType
IncorrectType 是我准备的自定义异常,选择是在同一文件中使用日期时间创建的(这无关紧要,因此我没有将其包含在代码中)。
我在 Flask 应用程序中的路线(简化):
#### EDIT: I pasted the wrong route, POST and GET Methods are included###
@edit_users.route('/users', methods=['GET', 'POST'])
def users():
... # Some code there
form = form_builder(roles_to_form)
print(form.is_submitted())
print(form.validate())
print(form.validate_on_submit())
return render_template('edit_user.html',
data_dict=data_dict, # in data_dict i pass form fields names and some other data
form=form,
)
我的模板:
<!-- Some usual stuff goes there css js etc -->
<div >
<form class="form form-horizontal" method="post" role="form" style="margin: auto; text-align: center; width: 40%;">
{{ form.csrf_token() }}
<table class="table" align="center">
<thead>
<th>Role</th>
<th>Expiration Date</th>
<th>Give Role?</th>
</thead>
{% for field in data_dict['id_list'] %}
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]] }}
</td>
<td align="left">{{ form[field[0]] }}</td>
</tr>
{% endfor %}
<tr>
<td colspan="3" align="center">{{ form.submit() }}</td>
</tr>
</table>
</form>
</div>
我的问题是什么?
当我点击“提交”按钮时,is_submitted()
returns 正确但 validate()
不正确,因此我不能使用典型的 if form.validated_on_submit()
因为它 returns即使我提交表单也是错误的。
我稍微挖掘了一下,发现了一些不寻常的东西。我使用了表单属性的受保护成员,来检查任何 validate() 函数视为输入:
for name in form._fields:
print(name)
inline = getattr(form.__class__, 'validate_%s' % name, None)
print(inline)
输出(不要介意字段名称):
提交
None
Engineer_2_bool
None
Engineer_2_date
None
Operator_3_bool
None
Operator_3_date
None
Supervisor_4_bool
None
Supervisor_4_date
None
Guest_5_bool
None
Guest_5_date
None
csrf_token
None
我不知道为什么我的表单没有 validate_{name}
属性。
我根据您的代码制作了一个工作示例,最终遇到了您描述的问题。
如果我把你的代码反推得很好,看来form[field[1]]
就是你的BooleanField或SelectField。因此,要在模板中呈现它,您必须使用 form[field[1]]()
(也可以使用 render_field)。
所以:
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]] }}
</td>
<td align="left">{{ form[field[0]] }}</td>
</tr>
更正为:
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]]() }}
</td>
<td align="left">{{ form[field[0]]() }}</td>
</tr>
https://flask.palletsprojects.com/en/1.1.x/patterns/wtforms/#forms-in-templates
当您使用 FlaskForm 时,您必须在模板中将 {{ form.csrf_token() }}
替换为 {{ form.csrf_token }}
https://flask-wtf.readthedocs.io/en/stable/csrf.html#html-forms
[编辑没有任何区别]
[根据 w8eight 的评论编辑]
该路由未获得 POST 的授权(并且表单发出 POST 请求,如 <form class="form form-horizontal" method="post" [...]
中所示。
所以你必须改变: @edit_users.route('/users', methods=['GET'])
到 @edit_users.route('/users', methods=['GET','POST'])
问题在于传递给 SelectField
选择的数据类型。我将带有 datetime.datetime
的元组列表传递给它。当将类型更改为 str
时,一切正常。
我的 Flask 应用程序中的 validate_on_submit() 方法存在以下问题。
快速概览我有什么:
我的表单 class 是根据输入动态创建的:
class EditUsers(FlaskForm):
submit = SubmitField('Submit')
def form_builder(roles_list):
class EditUsersForm(EditUsers):
pass
for role in roles_list:
if isinstance(role, tuple) and len(role) == 5:
if not role[2] and role[3]:
setattr(EditUsersForm, f'{role[0]}_{role[4]}_bool', BooleanField(label=role[0]))
setattr(EditUsersForm, f'{role[0]}_{role[4]}_date', SelectField('Expiration Period', choices=choices))
else:
raise IncorrectType
IncorrectType 是我准备的自定义异常,选择是在同一文件中使用日期时间创建的(这无关紧要,因此我没有将其包含在代码中)。
我在 Flask 应用程序中的路线(简化):
#### EDIT: I pasted the wrong route, POST and GET Methods are included###
@edit_users.route('/users', methods=['GET', 'POST'])
def users():
... # Some code there
form = form_builder(roles_to_form)
print(form.is_submitted())
print(form.validate())
print(form.validate_on_submit())
return render_template('edit_user.html',
data_dict=data_dict, # in data_dict i pass form fields names and some other data
form=form,
)
我的模板:
<!-- Some usual stuff goes there css js etc -->
<div >
<form class="form form-horizontal" method="post" role="form" style="margin: auto; text-align: center; width: 40%;">
{{ form.csrf_token() }}
<table class="table" align="center">
<thead>
<th>Role</th>
<th>Expiration Date</th>
<th>Give Role?</th>
</thead>
{% for field in data_dict['id_list'] %}
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]] }}
</td>
<td align="left">{{ form[field[0]] }}</td>
</tr>
{% endfor %}
<tr>
<td colspan="3" align="center">{{ form.submit() }}</td>
</tr>
</table>
</form>
</div>
我的问题是什么?
当我点击“提交”按钮时,is_submitted()
returns 正确但 validate()
不正确,因此我不能使用典型的 if form.validated_on_submit()
因为它 returns即使我提交表单也是错误的。
我稍微挖掘了一下,发现了一些不寻常的东西。我使用了表单属性的受保护成员,来检查任何 validate() 函数视为输入:
for name in form._fields:
print(name)
inline = getattr(form.__class__, 'validate_%s' % name, None)
print(inline)
输出(不要介意字段名称):
提交
None
Engineer_2_bool
None
Engineer_2_date
None
Operator_3_bool
None
Operator_3_date
None
Supervisor_4_bool
None
Supervisor_4_date
None
Guest_5_bool
None
Guest_5_date
None
csrf_token
None
我不知道为什么我的表单没有 validate_{name}
属性。
我根据您的代码制作了一个工作示例,最终遇到了您描述的问题。
如果我把你的代码反推得很好,看来form[field[1]]
就是你的BooleanField或SelectField。因此,要在模板中呈现它,您必须使用 form[field[1]]()
(也可以使用 render_field)。
所以:
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]] }}
</td>
<td align="left">{{ form[field[0]] }}</td>
</tr>
更正为:
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]]() }}
</td>
<td align="left">{{ form[field[0]]() }}</td>
</tr>
https://flask.palletsprojects.com/en/1.1.x/patterns/wtforms/#forms-in-templates
当您使用 FlaskForm 时,您必须在模板中将 {{ form.csrf_token() }}
替换为 {{ form.csrf_token }}
https://flask-wtf.readthedocs.io/en/stable/csrf.html#html-forms
[编辑没有任何区别]
[根据 w8eight 的评论编辑]
该路由未获得 POST 的授权(并且表单发出 POST 请求,如 <form class="form form-horizontal" method="post" [...]
中所示。
所以你必须改变: @edit_users.route('/users', methods=['GET'])
到 @edit_users.route('/users', methods=['GET','POST'])
问题在于传递给 SelectField
选择的数据类型。我将带有 datetime.datetime
的元组列表传递给它。当将类型更改为 str
时,一切正常。