Form Field Types
WTForms provides many field types.
Here are the most common ones you’ll use in Flask apps.
Text inputs
Section titled “Text inputs”StringFieldTextAreaFieldPasswordField
Numeric/date
Section titled “Numeric/date”IntegerFieldDecimalFieldDateField
EmailField(WTForms) orStringField+Email()validator
Choices
Section titled “Choices”SelectFieldRadioFieldSelectMultipleField
Example:
from wtforms import SelectField
role = SelectField(
"Role",
choices=[("user", "User"), ("admin", "Admin")],
)Checkboxes
Section titled “Checkboxes”BooleanField
File uploads
Section titled “File uploads”FileField
File uploads require enctype="multipart/form-data" in the HTML form.
Submit buttons
Section titled “Submit buttons”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:
| field | rendered HTML | raw 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> | text | str |
flowchart LR B["browser sends
everything as a STRING"] --> F["the field coerces it"] F --> S["StringField -> str"] F --> I["IntegerField -> int, or None if it will not parse"] F --> C["BooleanField -> True if present, False if absent"]
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.
Validators become HTML attributes
Section titled “Validators become HTML attributes”name = StringField("Your name", validators=[DataRequired(), Length(min=2)])renders, measured:
<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.
Passing extra attributes at render time
Section titled “Passing extra attributes at render time”form.name(class_="input", placeholder="Ada")<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.
See it move
Section titled “See it move”Check yourself
Section titled “Check yourself”-
A user leaves a BooleanField checkbox unchecked and submits. What does field.data hold?
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.
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.
-
StringField with DataRequired() and Length(min=2) renders required and minlength=2 in the HTML. What does that mean for security?
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.
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.
-
Why is the render-time keyword class_ spelled with a trailing underscore?
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.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.coffeeCtapch.feedbackHeading
pch.feedbackSubheading