In web development, forms are a crucial tool for collecting user information. Whether it’s for login/registration, feedback submission, or data collection, forms are an indispensable component. As a lightweight web framework, Flask can easily implement form creation, validation, and data processing when combined with the Flask-WTF extension. This article will guide you through the core process of handling Flask forms using a complete example.

I. Preparation: Install Required Tools

First, ensure Flask and Flask-WTF are installed. If not, you can install them via pip:

pip install flask flask-wtf

II. Step 1: Create a Form Class

The core of a form lies in defining the fields to collect and validation rules. By inheriting the FlaskForm class, you can utilize the field types (e.g., StringField, PasswordField) and built-in validators (e.g., DataRequired, Email) provided by Flask-WTF.

Example: Create a User Login Form

# forms.py file
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, ValidationError

class LoginForm(FlaskForm):
    # Username: required, length 4-20 characters
    username = StringField('Username', validators=[
        DataRequired(message='Username cannot be empty'),
        Length(min=4, max=20, message='Username must be between 4-20 characters')
    ])

    # Email: required, format validation
    email = StringField('Email', validators=[
        DataRequired(message='Email cannot be empty'),
        Email(message='Please enter a valid email address')
    ])

    # Password: required, minimum 8 characters
    password = PasswordField('Password', validators=[
        DataRequired(message='Password cannot be empty'),
        Length(min=8, message='Password must be at least 8 characters')
    ])

    # Submit button
    submit = SubmitField('Login')

III. Step 2: Write Flask Application and View Functions

In a Flask application, we need to handle two types of requests:
- GET Request: Render the form for user input
- POST Request: Receive form data, validate, and process it

Example: Main Application Code

# app.py file
from flask import Flask, render_template, redirect, url_for, flash
from forms import LoginForm  # Import the form class

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'  # Required for CSRF protection (use a random string in production)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()  # Instantiate the form class

    if form.validate_on_submit():  # Check if it's a POST request and data validation passes
        # Get form data
        username = form.username.data
        email = form.email.data
        password = form.password.data  # Encrypt passwords in production (for demonstration only)

        # Simulate data processing (e.g., validate user credentials, redirect after login)
        flash(f'Login successful! Welcome back, {username}')  # Show success message
        return redirect(url_for('dashboard'))  # Redirect to dashboard page

    return render_template('login.html', form=form)  # Render the form page

@app.route('/dashboard')
def dashboard():
    return render_template('dashboard.html')  # Data display page

if __name__ == '__main__':
    app.run(debug=True)

IV. Step 3: Design HTML Templates (Form Rendering and Data Display)

Templates are responsible for rendering forms on the frontend and displaying processing results. Use the form object’s methods to render form fields and include form.hidden_tag() to add the CSRF token (built-in protection by Flask-WTF).

1. Login Form Template (login.html)

<!-- templates/login.html -->
<!DOCTYPE html>
<html>
<head>
    <title>User Login</title>
    <style>
        .error { color: red; }
        .form-group { margin-bottom: 15px; }
    </style>
</head>
<body>
    <h1>User Login</h1>
    <!-- Display flash messages -->
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            {% for msg in messages %}
                <div style="color: green;">{{ msg }}</div>
            {% endfor %}
        {% endif %}
    {% endwith %}

    <!-- Render the form -->
    <form method="POST">
        {{ form.hidden_tag() }}  <!-- Required for CSRF protection -->

        <!-- Username -->
        <div class="form-group">
            {{ form.username.label }}<br>
            {{ form.username(size=32, class="form-control") }}  <!-- Render input field -->
            {% for error in form.username.errors %}  <!-- Display validation errors -->
                <span class="error">{{ error }}</span>
            {% endfor %}
        </div>

        <!-- Email -->
        <div class="form-group">
            {{ form.email.label }}<br>
            {{ form.email(size=32, class="form-control") }}
            {% for error in form.email.errors %}
                <span class="error">{{ error }}</span>
            {% endfor %}
        </div>

        <!-- Password -->
        <div class="form-group">
            {{ form.password.label }}<br>
            {{ form.password(size=32, class="form-control") }}
            {% for error in form.password.errors %}
                <span class="error">{{ error }}</span>
            {% endfor %}
        </div>

        <!-- Submit button -->
        {{ form.submit(class="btn btn-primary") }}
    </form>
</body>
</html>

2. Data Display Template (dashboard.html)

<!-- templates/dashboard.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Login Successful</title>
</head>
<body>
    <h1>Welcome to Dashboard</h1>
    <p>Here you can view user data, for example:</p>
    <ul>
        <li>Username: {{ session.get('username') }}</li>  <!-- Assume username is stored in session -->
        <li>Email: {{ session.get('email') }}</li>
    </ul>
</body>
</html>

V. Key Details Explained

1. CSRF Protection

Flask-WTF enables CSRF protection by default, automatically generating a CSRF token via form.hidden_tag(). This tag must be included in the template; otherwise, form submissions will fail.

2. Data Validation

form.validate_on_submit() automatically executes all validation rules. If validation fails, error messages are stored in form.<field_name>.errors and displayed in the template via loops.

3. Avoid Duplicate Submissions

Use redirects (e.g., redirect(url_for('dashboard'))) instead of directly returning templates to prevent duplicate submissions when users refresh the page.

4. Data Storage

In the example, form.<field>.data is retrieved directly. In production, data should be saved to a database (e.g., with SQLAlchemy) and passwords should be encrypted (e.g., using bcrypt).

VI. Complete Process Summary

  1. User Fills Form: Data is entered via the form rendered in login.html
  2. Frontend Validation: The browser automatically checks required fields and formats (e.g., email format)
  3. Backend Validation: Flask-WTF validates data integrity (e.g., length, format)
  4. Data Processing: After validation, data is retrieved and processed (e.g., login authentication)
  5. Result Display: Processed data is shown via dashboard.html

VII. Advanced Tips

  • Custom Validators: For complex validation (e.g., checking if a username exists), define methods in the form class:
  def validate_username(self, field):
      if User.query.filter_by(username=field.data).first():
          raise ValidationError('Username is already registered')
  • Multi-Form Handling: Use FlaskForm to create multiple form types and merge data with form = MultiForm(request.form)
  • File Uploads: Combine FileField with FileAllowed validators for image/file uploads

By following these steps, you’ve mastered the core process of handling Flask forms. From form definition to data display, each step ensures user experience and data security. Extend functionality in practice as needed, such as adding database interactions or permission controls.

Xiaoye