Skip to content

Form Field Types

WTForms provides many field types.

Here are the most common ones you’ll use in Flask apps.

  • StringField
  • TextAreaField
  • PasswordField
  • IntegerField
  • DecimalField
  • DateField
  • EmailField (WTForms) or StringField + Email() validator
  • SelectField
  • RadioField
  • SelectMultipleField

Example:

python
from wtforms import SelectField
 
role = SelectField(
    "Role",
    choices=[("user", "User"), ("admin", "Admin")],
)
  • BooleanField
  • FileField

File uploads require enctype="multipart/form-data" in the HTML form.

  • SubmitField

Tip: keep only one submit field per form unless you really need multiple actions.

Each field type decides the HTML and the Python type

Section titled “Each field type decides the HTML and the Python type”

Measured — the exact HTML each field renders, and what field.data holds after a valid submission:

fieldrendered HTMLraw submitted.data
StringField<input type="text">'ada''ada' (str)
IntegerField<input type="number">'42'42 (int)
PasswordField<input type="password">'s3cret''s3cret' (str)
BooleanField<input type="checkbox" value="y">'y'True (bool)
SelectField<select><option>...'a''a' (str)
TextAreaField<textarea>textstr
diagram Diagram mermaid

The whole point of the field type is that HTTP has no types. Every value arrives as a string, and the field is what turns '42' into 42 — and what reports 'Not a valid integer value.' when it cannot.

attrs.py
name = StringField("Your name", validators=[DataRequired(), Length(min=2)])

renders, measured:

output
<input id="name" minlength="2" name="name" required type="text" value="">

DataRequired() produced required and Length(min=2) produced minlength="2". The browser will now enforce both before the request is sent.

render.py
form.name(class_="input", placeholder="Ada")
measured
<input class="input" id="name" minlength="2" name="name"
       placeholder="Ada" required type="text" value="">

class_ has the trailing underscore because class is a Python keyword — the same convention for_ uses on labels.

sketch From HTML control to Python value p5.js
Every value arrives as a string. The field type decides both the control rendered and the type the value is coerced to.
pch.quizTag pch.quizDefaultTitle
  1. A user leaves a BooleanField checkbox unchecked and submits. What does field.data hold?

    pch.quizShowAnswer

    B — False, because an unchecked box is omitted from the request entirely — The browser sends nothing for an unchecked box, and BooleanField maps absence to False. That is also why DataRequired on a checkbox forces the user to tick it.

  2. StringField with DataRequired() and Length(min=2) renders required and minlength=2 in the HTML. What does that mean for security?

    pch.quizShowAnswer

    B — nothing changes on the server; those attributes are generated from the validators and can be stripped or bypassed by any client — The attributes are a convenience for honest users. A POST from curl ignores them completely, so the server-side validators remain the only real check.

  3. Why is the render-time keyword class_ spelled with a trailing underscore?

    pch.quizShowAnswer

    B — because class is a reserved Python keyword and cannot be used as an argument name — class is one of the 35 reserved keywords, so the trailing-underscore convention is used, exactly as for_ on labels. It renders as class in the HTML.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading