Search code examples
pythonpython-3.xflaskflask-wtformswtforms

WTForms Validators not Working except DataRequired()


I'm building a website using Python, Flask, and WTForms. I built a form webpage and tried to add Validators to the form. However, I found that only DataRequired() is working. The other validators aren't reporting any error even when I deliberately input wrong values.

forms.py

from flask_wtf import FlaskForm,Form
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired, IPAddress

class FooForm(Form): # I tried both FlaskForm and Form
    ip = StringField('IP Address', validators=[DataRequired(),IPAddress()])

    button = SubmitField('Submit')

main.py

@app.route('/foo', methods=['POST', 'GET'])
def foo():
    foo_form = FooForm()

    return render_template('foo.html', form=foo_form)

/templates/foo.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>Foo</h2>
<form method="post">
    {{ form.ip.label() }}
    {{ form.ip() }}

    {{ form.button.label() }}
    {{ form.button() }}
</form>
</body>
</html>

Example GIF: enter image description here

As you can see, DataRequired() is working, but not so for IPAddress(), plus basically any other validators I've tried.

{% form.ip.error %} also is empty.

Anyone know why this is happening? Or should I just build a form with scratch HTML?

Python 3.10.8; Flask 2.2.2; Flask-WTF 1.0.1; WTForms 3.0.1


Solution

  • They are working, but you are not validating the form with foo_form.validate_on_submit().

    Some validations are setting the attributes for the <input> tags. Therefore they can be instantly validated on the html side. Others are not setting them and only validate the input on the backend. Look for the following statement under wtforms documentation:

    Sets the required attribute on widgets.

    Length is something that can be set via an attribute, however IPAddress requires you to use validate_on_submit().

    class FooForm(FlaskForm):
        ip = StringField("IP Address", validators=[DataRequired(), IPAddress()])
        button = SubmitField("Submit")
    
    
    @app.route("/foo", methods=["POST", "GET"])
    def foo():
        foo_form = FooForm()
        if foo_form.validate_on_submit():
            return "validated"
    
        print(foo_form.errors)
        return render_template("foo.html", form=foo_form)
    
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    
    <body>
      <h2>Foo</h2>
      <form method="post">
        {{ form.hidden_tag() }}  <!-- Make sure to include this line for CSRF protection -->
        {{ form.ip.label() }}
        {{ form.ip() }}
    
        {{ form.button.label() }}
        {{ form.button() }}
      </form>
    </body>
    
    </html>
    
    # Output
    127.0.0.1 - - [14/Dec/2022 11:37:16] "GET / HTTP/1.1" 200 -
    {'ip': ['Invalid IP address.']}