Skip to main content

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:

bash
pip install Flask-WTF

Basic Form Structure

A typical Flask-WTF form class looks like this:

python
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.

python
from wtforms import StringField

name = StringField('Name', validators=[DataRequired()])

TextAreaField

For multi-line text input, use TextAreaField:

python
from wtforms import TextAreaField

description = TextAreaField('Description')

PasswordField

For password input (text is masked):

python
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:

python
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:

python
from wtforms import IntegerField

age = IntegerField('Age', validators=[DataRequired()])

FloatField

For decimal numbers:

python
from wtforms import FloatField

height = FloatField('Height (m)', validators=[DataRequired()])

DecimalField

For precise decimal values:

python
from wtforms import DecimalField

price = DecimalField('Price', places=2, validators=[DataRequired()])

Date and Time Fields

DateField

For date input:

python
from wtforms import DateField

birth_date = DateField('Date of Birth', format='%Y-%m-%d')

DateTimeField

For date and time input:

python
from wtforms import DateTimeField

appointment = DateTimeField('Appointment Date and Time', format='%Y-%m-%d %H:%M')

Example: Event Creation Form

python
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:

python
from wtforms import SelectField

country = SelectField('Country', choices=[
('us', 'United States'),
('ca', 'Canada'),
('uk', 'United Kingdom')
])

SelectMultipleField

For multiple selections:

python
from wtforms import SelectMultipleField

interests = SelectMultipleField('Interests', choices=[
('tech', 'Technology'),
('sports', 'Sports'),
('music', 'Music'),
('reading', 'Reading')
])

RadioField

For radio button options:

python
from wtforms import RadioField

gender = RadioField('Gender', choices=[
('male', 'Male'),
('female', 'Female'),
('other', 'Other')
])

BooleanField

For checkboxes:

python
from wtforms import BooleanField

subscribe = BooleanField('Subscribe to newsletter')

Example: Survey Form

python
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:

python
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:

python
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload

Example: File Upload Form

python
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:

python
from wtforms import HiddenField

user_id = HiddenField()

Field Customization

You can customize fields by adding attributes:

python
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:

python
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:

python
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:

html
<!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:

  1. Text Input Fields: StringField, TextAreaField, PasswordField
  2. Numeric Fields: IntegerField, FloatField, DecimalField
  3. Date/Time Fields: DateField, DateTimeField
  4. Selection Fields: SelectField, SelectMultipleField, RadioField, BooleanField
  5. File Upload Fields: FileField
  6. 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

  1. Create a contact form with name, email, subject, and message fields.
  2. Build a product creation form with fields for name, description, price, category (dropdown), and image upload.
  3. Design a user settings form that allows updating profile information and preferences.
  4. Create a survey form with different question types (text, multiple choice, checkboxes).
  5. 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! :)