Flask Form Fields
Forms are a crucial component of web applications, allowing users to input data that can be processed by the server. Flask, combined with the Flask-WTF extension, provides a powerful way to create and validate forms. In this guide, we'll explore the various form fields available in Flask-WTF and how to use them effectively.
Introduction to Flask-WTF Form Fields
Flask-WTF is an integration of WTForms for Flask. It provides various field types that you can use in your forms, ranging from simple text fields to complex file upload fields.
Before diving into specific fields, make sure you have Flask-WTF installed:
pip install Flask-WTF
Basic Form Structure
A typical Flask-WTF form class looks like this:
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class SimpleForm(FlaskForm):
name = StringField('Name', validators=[DataRequired()])
submit = SubmitField('Submit')
This creates a simple form with a text input field for a name and a submit button.
Text Input Fields
StringField
The most basic input field is StringField
, which renders as an HTML <input type="text">
element.
from wtforms import StringField
name = StringField('Name', validators=[DataRequired()])
TextAreaField
For multi-line text input, use TextAreaField
:
from wtforms import TextAreaField
description = TextAreaField('Description')
PasswordField
For password input (text is masked):
from wtforms import PasswordField
password = PasswordField('Password', validators=[DataRequired()])
Example: User Registration Form
Here's a practical example of a registration form using various text fields:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Email, Length, EqualTo
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=20)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=8)])
confirm_password = PasswordField(
'Confirm Password',
validators=[DataRequired(), EqualTo('password', message='Passwords must match')]
)
bio = TextAreaField('Bio', validators=[Length(max=200)])
submit = SubmitField('Register')
Numeric Fields
IntegerField
For integer input:
from wtforms import IntegerField
age = IntegerField('Age', validators=[DataRequired()])
FloatField
For decimal numbers:
from wtforms import FloatField
height = FloatField('Height (m)', validators=[DataRequired()])
DecimalField
For precise decimal values:
from wtforms import DecimalField
price = DecimalField('Price', places=2, validators=[DataRequired()])
Date and Time Fields
DateField
For date input:
from wtforms import DateField
birth_date = DateField('Date of Birth', format='%Y-%m-%d')
DateTimeField
For date and time input:
from wtforms import DateTimeField
appointment = DateTimeField('Appointment Date and Time', format='%Y-%m-%d %H:%M')
Example: Event Creation Form
from flask_wtf import FlaskForm
from wtforms import StringField, DateTimeField, TextAreaField, DecimalField, SubmitField
from wtforms.validators import DataRequired, Length
class EventForm(FlaskForm):
title = StringField('Event Title', validators=[DataRequired(), Length(max=100)])
description = TextAreaField('Description', validators=[Length(max=500)])
start_time = DateTimeField('Start Time', format='%Y-%m-%d %H:%M', validators=[DataRequired()])
end_time = DateTimeField('End Time', format='%Y-%m-%d %H:%M', validators=[DataRequired()])
price = DecimalField('Ticket Price', places=2, validators=[DataRequired()])
submit = SubmitField('Create Event')
Selection Fields
SelectField
For dropdown selection:
from wtforms import SelectField
country = SelectField('Country', choices=[
('us', 'United States'),
('ca', 'Canada'),
('uk', 'United Kingdom')
])
SelectMultipleField
For multiple selections:
from wtforms import SelectMultipleField
interests = SelectMultipleField('Interests', choices=[
('tech', 'Technology'),
('sports', 'Sports'),
('music', 'Music'),
('reading', 'Reading')
])
RadioField
For radio button options:
from wtforms import RadioField
gender = RadioField('Gender', choices=[
('male', 'Male'),
('female', 'Female'),
('other', 'Other')
])
BooleanField
For checkboxes:
from wtforms import BooleanField
subscribe = BooleanField('Subscribe to newsletter')
Example: Survey Form
from flask_wtf import FlaskForm
from wtforms import StringField, RadioField, SelectMultipleField, BooleanField, SubmitField
from wtforms.validators import DataRequired
class SurveyForm(FlaskForm):
name = StringField('Name', validators=[DataRequired()])
age_group = RadioField('Age Group', choices=[
('under18', 'Under 18'),
('18-25', '18 to 25'),
('26-35', '26 to 35'),
('36-50', '36 to 50'),
('over50', 'Over 50')
], validators=[DataRequired()])
platforms = SelectMultipleField('What platforms do you use?', choices=[
('windows', 'Windows'),
('macos', 'MacOS'),
('linux', 'Linux'),
('android', 'Android'),
('ios', 'iOS')
])
agree_terms = BooleanField('I agree to the terms and conditions', validators=[DataRequired()])
submit = SubmitField('Submit')
File Upload Fields
FileField
For file uploads:
from flask_wtf.file import FileField, FileRequired, FileAllowed
profile_pic = FileField('Profile Picture', validators=[
FileRequired(),
FileAllowed(['jpg', 'png'], 'Images only!')
])
To handle file uploads, you'll need to configure Flask:
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload
Example: File Upload Form
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import StringField, SubmitField
class UploadForm(FlaskForm):
title = StringField('Document Title')
document = FileField('Upload Document', validators=[
FileRequired(),
FileAllowed(['pdf', 'doc', 'docx'], 'Documents only!')
])
submit = SubmitField('Upload')
Hidden Fields
For fields that shouldn't be visible but need to be submitted:
from wtforms import HiddenField
user_id = HiddenField()
Field Customization
You can customize fields by adding attributes:
from wtforms import StringField
name = StringField('Name', render_kw={
"placeholder": "Enter your full name",
"class": "form-control",
"autocomplete": "off"
})
Dynamic Field Choices
Sometimes, you need to load field choices from a database:
class CourseForm(FlaskForm):
teacher = SelectField('Teacher', coerce=int)
def __init__(self, *args, **kwargs):
super(CourseForm, self).__init__(*args, **kwargs)
self.teacher.choices = [(t.id, t.name) for t in Teacher.query.all()]
Full Example: Putting It All Together
Here's a complete example of a Flask application with a form containing various field types:
from flask import Flask, render_template, redirect, url_for, flash
from flask_wtf import FlaskForm
from wtforms import (StringField, TextAreaField, IntegerField,
SelectField, RadioField, BooleanField, SubmitField)
from wtforms.validators import DataRequired, Length, Email, NumberRange
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
class ProfileForm(FlaskForm):
name = StringField('Full Name', validators=[DataRequired(), Length(min=2, max=100)])
email = StringField('Email', validators=[DataRequired(), Email()])
age = IntegerField('Age', validators=[NumberRange(min=18, max=120)])
occupation = SelectField('Occupation', choices=[
('developer', 'Software Developer'),
('designer', 'Designer'),
('manager', 'Manager'),
('student', 'Student'),
('other', 'Other')
])
experience_level = RadioField('Experience Level', choices=[
('beginner', 'Beginner'),
('intermediate', 'Intermediate'),
('expert', 'Expert')
])
programming_languages = SelectField('Favorite Programming Language', choices=[
('python', 'Python'),
('javascript', 'JavaScript'),
('java', 'Java'),
('cpp', 'C++'),
('other', 'Other')
])
subscribe = BooleanField('Subscribe to newsletter')
bio = TextAreaField('Bio', validators=[Length(max=500)])
submit = SubmitField('Save Profile')
@app.route('/', methods=['GET', 'POST'])
def profile():
form = ProfileForm()
if form.validate_on_submit():
flash('Profile updated successfully!', 'success')
# In a real app, you would save the form data to a database here
return redirect(url_for('profile'))
return render_template('profile.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
And here's what the template might look like:
<!DOCTYPE html>
<html>
<head>
<title>User Profile</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-5">
<h1>User Profile</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.name.label(class="form-label") }}
{{ form.name(class="form-control") }}
{% if form.name.errors %}
<div class="text-danger">
{% for error in form.name.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.email.label(class="form-label") }}
{{ form.email(class="form-control") }}
{% if form.email.errors %}
<div class="text-danger">
{% for error in form.email.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.age.label(class="form-label") }}
{{ form.age(class="form-control") }}
{% if form.age.errors %}
<div class="text-danger">
{% for error in form.age.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.occupation.label(class="form-label") }}
{{ form.occupation(class="form-select") }}
</div>
<div class="mb-3">
{{ form.experience_level.label(class="form-label") }}<br>
{% for option in form.experience_level %}
<div class="form-check form-check-inline">
{{ option(class="form-check-input") }}
{{ option.label(class="form-check-label") }}
</div>
{% endfor %}
</div>
<div class="mb-3">
{{ form.programming_languages.label(class="form-label") }}
{{ form.programming_languages(class="form-select") }}
</div>
<div class="mb-3 form-check">
{{ form.subscribe(class="form-check-input") }}
{{ form.subscribe.label(class="form-check-label") }}
</div>
<div class="mb-3">
{{ form.bio.label(class="form-label") }}
{{ form.bio(class="form-control", rows=5) }}
{% if form.bio.errors %}
<div class="text-danger">
{% for error in form.bio.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
</div>
</body>
</html>
Summary
Flask-WTF provides a comprehensive set of form fields to handle various types of user input:
- Text Input Fields: StringField, TextAreaField, PasswordField
- Numeric Fields: IntegerField, FloatField, DecimalField
- Date/Time Fields: DateField, DateTimeField
- Selection Fields: SelectField, SelectMultipleField, RadioField, BooleanField
- File Upload Fields: FileField
- Hidden Fields: HiddenField
By utilizing these fields along with validators, you can create robust forms that collect and validate user input efficiently.
Additional Resources
Exercises
- Create a contact form with name, email, subject, and message fields.
- Build a product creation form with fields for name, description, price, category (dropdown), and image upload.
- Design a user settings form that allows updating profile information and preferences.
- Create a survey form with different question types (text, multiple choice, checkboxes).
- Implement a dynamic form where the available options in one dropdown depend on the selection in another.
By practicing with these exercises, you'll gain proficiency in creating and handling Flask forms for various scenarios in your web applications.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)