Flask - wtforms: 验证始终为假

17

首先,我是 Python 和 Flask 的新手,所以如果我的问题很愚蠢,我很抱歉。尽管我尝试搜索解答,但我从未找到过(这应该是一个“简单”的答案)。

我想在我的网站上添加一个联系页面,所以我找到了这个教程,然后按照它的步骤进行操作。一切都很好,直到表单验证的时候。我只使用Required,但是form.validate()总是返回 false。如果我不改动代码,并且将表单类中的每一个 Required 都移除,那么就能正常工作,form.validate() 返回 true。

我真的不明白为什么,我看了很多人说要使用validate_on_submit(),但是如果我使用它,就会出现错误:*'ClassName' object has no attribute 'validate_on_submit'*。

以下是代码相关部分:

Index.py

@app.route('/contact', methods=['GET','POST'])
def contact():
form = ContactForm()

if request.method == 'POST':
    if form.validate() == False:
        flash('All Fields are required.')
        return render_template('contact.html', form=form)
    else:
        return 'Form posted'
elif request.method == 'GET':
    return render_template('contact.html', form=form)

forms.py

from wtforms import Form, TextField, TextAreaField, SubmitField, validators,ValidationError 

class ContactForm(Form):
  name = TextField("Name", [validators.Required()])
  email = TextField("Email")
  subject = TextField("Subject")
  message = TextAreaField("Message")
  submit = SubmitField("Send")

联系我们.html

<div id="contact">
    {% for message in get_flashed_messages() %}
        <div class="flash">{{ message }}</div>
    {% endfor %}
  <form action="{{ url_for('contact') }}" method=post>

    {{ form.name.label }}
    {{ form.name }}

    {{ form.email.label }}
    {{ form.email }}

    {{ form.subject.label }}
    {{ form.subject }}

    {{ form.message.label }}
    {{ form.message }}

    {{ form.submit }}
  </form>
 </div>

即使在姓名字段中填写了内容,我也从未收到“表单已提交”的字符串。

提前致谢。

4个回答

39

在按照Miguel Grinberg的书《Flask Web Development》中的演示代码测试登录表单时,我总是无法通过form.validate_on_submit()。因此,我认为我应该找到一种调试方法。

我采取的调试方法是将以下代码添加到app/auth/views.py中:

flash(form.errors)

当我跑到登录页面时,它会向我显示罪犯:

errors={'csrf_token': ['CSRF token missing']}

所以我建议使用 form.errors 消息进行调试。


1
这是我的错误。我使用了form.hidden_field而不是form.hidden_tag()。 - Seeni
1
你救了我的命。 - Yiling Liu
我刚开始使用Flask进行开发,不知道如何进行调试。谢谢您的帮助,这里有一个使用示例:https://dev59.com/c2Yr5IYBdhLWcg3wi63d#13587339 - xtian
通过您的提示,我得到了错误信息:“CSRF 令牌不匹配”。我在网上搜索过,但并没有解决我的问题。您对这个问题有什么想法吗?我已经通过“{{ form.hidden_tag() }}”和“{{ form.csrf_token }}”在表单中设置了 csrf。 - Hosein Aqajani

16
您需要使用请求中的值对表单实例进行初始化:
from flask import request

@app.route('/contact', methods=['GET','POST'])
def contact():
    form = ContactForm(request.form)
    if request.method == "POST" and form.validate():
        # do something with form
        # and probably return a redirect
    return render_template("contact.html", form=form)

这里有一个比你在问题中提供的教程更好的教程:http://flask.pocoo.org/docs/patterns/wtforms/.

请查看教程中的模板渲染代码,确保您呈现表单字段错误。如果表单已发布但未经过验证,则代码将继续执行 render_template ,并包含字段验证错误的表单实例(有关详细信息,请参见教程和WTForms文档)。


1
这个不再修复问题了。请参考下面与CSRF令牌相关的修复方法。 - Sean Pianka

12

刚遇到这个问题,解决方法是在模板中表单下面添加hidden_tag

...
<form action="{{ url_for('contact') }}" method=post>
{{ form.hidden_tag() }}
...

1
@SARose 这个方法可行是因为否则会出现这种情况:https://dev59.com/fmEi5IYBdhLWcg3w2POB#34570528 - 由于缺少CSRF令牌,验证失败。因此,为了修复它,我们需要将CSRF令牌作为隐藏输入值插入到表单中。所以 {{ form.hidden_tag() }} 实际上会将类似以下内容插入HTML中:<input type="hidden" name="csrf-field-name" value="csrf-token-here">。现在,csrf-token随表单一起发送,验证通过。 - Pavel Vergeev

4

像@Paul Vergeev所说,只需添加:


<form action="{{ url_for('contact') }}" method=post>
{{ form.csrf_token }}

这是必需的,因为你的表单包含验证器,因此需要在你的html表单中添加一个类型为“Hidden”的“input”。如果没有这个隐藏的令牌在你的html中,验证器将无法验证用户的输入,验证的结果将始终为False。

{{ form.hidden_tag() }}

这将包括您HTML代码中的所有隐藏输入字段;但它们不会显示在页面上。

{{ form.csrf_token }}

在你的HTML代码中,只会包含隐藏的csrf_token输入字段;但它们不会显示在页面上。

还有一件事要做:您必须配置应用程序的SECRET_KEY。这可以通过在应用程序初始化后(即在app = Flask(__name__)之后)包含app.config ["SECRET_KEY"] =" a secret key you won't forget "来完成。

所有这些都是WTForms验证器保护您的站点免受CSRF(通常发音为c-surf)攻击的方法。您也可以在此处阅读有关CSRF的更多信息。


1
谢谢 @John Mutuma ... 这节省了我的时间 :) - Kavin Raju S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接