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:

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:

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