【问题标题】:Flask WTForms always give false on validate_on_submit()Flask WTForms 在 validate_on_submit() 上总是给出 false
【发布时间】:2013-09-14 01:05:00
【问题描述】:

我使用 wtforms 创建了一个注册表单。我在其中使用了 FormField,这样我就不必再次重复表单的某些元素。但是每当我点击提交按钮时,它总是在 validate_on_submit 方法调用上给我错误。不明白为什么会发生这种情况。

我的form.py如下:

class ProfileInfoForm(Form):
    firstname = TextField('firstname', validators=
                          [validators.Required("Please enter First name.")])
    lastname = TextField('lastname', validators=
                         [validators.Required("Please enter Last name.")])
    email = EmailField('email', validators=
                       [validators.Required("Please enter your valid email.")])
    gender = RadioField('gender', validators=
                        [validators.Required("Please select gender")],
                        choices=[('female', 'Female'), ('male', 'Male')])
    dob = TextField('dob', validators=
                    [validators.Required("Please select date of birth.")])
    languages = SelectMultipleField('languages', choices=[('', '')],
                                    validators=
                                    [validators.Required("Please select\
                                                         atleast one \
                                                         language.")])


class RegistrationForm(Form):
    profilefield = FormField(ProfileInfoForm)
    password = PasswordField('password',
                             validators=
                             [validators.Required("Please enter password."),
                              validators.Length(min=8),
                              validators.EqualTo('confirm_password',
                                                 message='Password and confirm\
                                                 password must match')])
    confirm_password = PasswordField('confirm_password',
                                     validators=
                                     [validators.Required("Please enter\
                                                          confirm password.")])
    tnc = BooleanField('tnc', validators=
                       [validators.Required("Please select Terms and \
                                            Conditions")], default=False)

    submit = SubmitField('Create My Account')

Signup方法如下:

@module.route('/signup', methods=['GET', 'POST'])
  @handle_error
  def signup():
      if hasattr(g, 'user') and g.user:
          # TODO: do some operations if needed else keep it blank
          return redirect(url_for('index'))
      else:
          signup_form = RegistrationForm()
          # Add choices for the user
          signup_form.profilefield.languages.choices = getLanguages()
          if signup_form.validate_on_submit():
              firstname = signup_form.profilefield.firstname.data
              lastname = signup_form.profilefield.lastname.data
              email = signup_form.profilefield.email.data
              password = signup_form.password.data
              #  confirm_password = signup_form.confirm_password.data
              gender = signup_form.profilefield.gender.data
              dob = signup_form.profilefield.dob.data
              languages = signup_form.profilefield.languages.data
              tnc = signup_form.tnc.data

              payload = {'firstname': firstname, 'lastname': lastname,
                         'email': email, 'password': password, 'gender': gender,
                         'dob': dob, 'languages': languages,
                         'tnc': ('1' if tnc else '0')}
              try:
                  buildApiUrl = BuildApiUrl()
                  response = requests.post(buildApiUrl.getUrl("user", "signup"),
                                           data=payload)

                  if response.status_code == requests.codes.ok:
                      data = json.loads(response.text)
                      if 'status' in data and data['status'] != 200:
                          flash(data['message'], category="error")
                      else:
                          flash(data['message'] +
                                ': Your account is created successfully! ' +
                                'Please login to your account!',
                                category="success")
                          return redirect(url_for('index'))
              except requests.exceptions.RequestException:
                  flash('Internal Server side error occured', category="error")
                  return redirect(url_for('server_error', e='500'))

      return render_template('public/index.html',
                             signup_form=signup_form, login_form=LoginForm())

HTML 表单存在于gist here

仅供参考:我将所有必填字段与所需的实际数据放在一起。当我调用 validate_on_submit() 时仍然会出错。我的代码有什么问题?

编辑:getLanguages 是一种从数据库中检索语言并放入选择列表的方法。此功能正在按预期进行,我可以获得语言列表。

编辑 2:在这里实现一件事。这是由于 FormField 造成的,因为我通过将 ProfileInfoForm() 的所有字段添加到 RegistrationForm() 方法中进行了测试,一切正常,我可以注册。因此,FormField 或我使用它的方式存在一些问题,但不确定哪里出了问题。

发现问题不在于 FormField,而在于我的 ProfileInfoForm()。它总是返回假。还没有理由,但我想我可能必须为此编写自己的验证。有什么想法吗?

编辑:

转储时我得到以下信息(此处使用 pprint):

{'SECRET_KEY': '1e4c35233e50840483467e8d6cfe556c',
 '_errors': None,
 '_fields': {'csrf_token': <wtforms.ext.csrf.fields.CSRFTokenField object at 0x2207290>,
             'dob': <wtforms.fields.simple.TextField object at 0x2207650>,
             'email': <flask_wtf.html5.EmailField object at 0x22074d0>,
             'firstname': <wtforms.fields.simple.TextField object at 0x2207350>,
             'gender': <wtforms.fields.core.RadioField object at 0x2207590>,
             'languages': <wtforms.fields.core.SelectMultipleField object at 0x2207710>,
             'lastname': <wtforms.fields.simple.TextField object at 0x2207410>},
 '_prefix': u'profilefield-',
 'csrf_enabled': True,
 'csrf_token': <wtforms.ext.csrf.fields.CSRFTokenField object at 0x2207290>,
 'dob': <wtforms.fields.simple.TextField object at 0x2207650>,
 'email': <flask_wtf.html5.EmailField object at 0x22074d0>,
 'firstname': <wtforms.fields.simple.TextField object at 0x2207350>,
 'gender': <wtforms.fields.core.RadioField object at 0x2207590>,
 'languages': <wtforms.fields.core.SelectMultipleField object at 0x2207710>,
 'lastname': <wtforms.fields.simple.TextField object at 0x2207410>}

编辑:

我稍微挖掘了一下,发现产生错误是由于缺少csrf令牌。但是我在我的表单模板中包含了{{ signup_form.hidden_tag() }} html。当我检查元素时,我可以看到生成的 html 中的隐藏标签,并且可以看到带有哈希值的 csrf_token 字段。那么这里有什么问题呢?

【问题讨论】:

  • 没有人遇到过这个问题?
  • 你能添加一个进入视图的数据的转储吗?
  • 我没有收到您的问题?你能详细说明你想让我给什么吗?就我的代码而言,视图检查 validate_on_submit 响应始终为假,因此跳过该部分并再次呈现我的注册页面。
  • 我想在验证之前查看request.form中的数据。
  • 我在编辑中添加了 pprint 的输出。请看一下

标签: python flask wtforms flask-wtforms


【解决方案1】:

我遇到了同样的问题,我能够解决它。

问题与 LoginForm 的 id 和 username 和验证器有关,而 html 表单不需要这些信息

            <h1>Login</h1>

            <form action="" method="POST"  name="login">
        {{ login_form.csrf_token }}
        {{ login_form.hidden_tag() }}

        <p>
            {{ login_form.email.label }}<br>
            {{ login_form.email(size=64) }}<br>
            {% for error in login_form.email.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>
            {{ login_form.password.label }}<br>
            {{ login_form.password(size=32) }}<br>
            {% for error in login_form.password.errors %}
            <span style="color: red;">[{{ error }}]</span>
            {% endfor %}
        </p>
        <p>{{ login_form.remember_me }} Remember Me</p>
{#            <input type="submit" value="Sign In">#}
        <p>{{ login_form.submit() }}</p>
    </form>



class LoginForm(FlaskForm):
    ***# user_id = StringField('user_id',validators=[DataRequired()])
    # user_name = StringField('user_name',validators=[DataRequired(), Length(min=3, max=20)])***
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember_me = BooleanField('remember_me', default=False)
    submit = SubmitField('LogIn')

【讨论】:

    【解决方案2】:

    我用以下函数解决了我的问题:

    def __init__(self, *args, **kwargs):
        kwargs['csrf_enabled'] = False
        super(ProfileInfoForm, self).__init__(*args, **kwargs)
    

    我在ProfileInfoForm()添加了这个功能

    问题是FormField 包括csrf_token 字段以及实际形式,即RegistrationForm 还包括csrf_token,因此有两个csrf_token 需要验证,只有一个实际呈现在形式。所以,我在ProfileInfoForm 中禁用了csrf_token,所以当FormField 渲染它时,它有csrf_token = False

    并且RegistrationForm 确实现在仍然启用了csrf_token,因此表单仍然是安全的。

    我猜这也需要在FormField 中完成。

    仅供参考:由于我对 FormField 代码的解释,此解决方案可能是错误的。所以如果我在上述解决方案中有错误,请纠正我。

    【讨论】:

    • 5 年半后,这是唯一有帮助的答案!
    猜你喜欢
    • 2013-12-08
    • 2018-07-26
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多