Hello! Let's continue our journey into web development with Python.
In the last lesson, you learned how to structure a Flask application with routes, dynamic URLs, and Jinja2 templates. You can now build multi-page websites that display static or dynamic data. This is a great foundation, but modern web applications are interactive; they need to listen and respond to users.
This lesson focuses on exactly that. Our goal is to handle form submissions and validate user input in Flask. You will learn how to build forms for tasks like user registration or login, receive the data a user enters, check if it's valid, and provide clear feedback. This is a cornerstone skill for building any user-facing application.
1. The Language of the Web: GET and POST Requests
Before we build a form, we need to understand how data travels from a user's browser to our Flask application. Web browsers and servers primarily communicate using HTTP methods. The two most common methods are GET and POST.
GET: Used to request data from a server. When you type a URL into your browser or click a normal link, you are making aGETrequest. Any data sent is appended to the URL (e.g.,/search?q=flask), making it visible and insecure for sensitive information.POST: Used to send data to a server to create or update a resource. When you submit a form with a password, the data is sent in the body of the HTTP request, not the URL. This is the standard method for handling form submissions.
To understand this crucial distinction, watch the first part of the following video.
Flask Tutorial #4 - HTTP Methods (GET/POST) & Retrieving Form Data
This video from Tech With Tim clearly explains the fundamental differences between GET and POST requests.
Watch the first 2 minutes (00:00 - 02:09). Focus on understanding why you would choose POST over GET for form submissions, especially regarding security.
To handle a form submission, our Flask route needs to be configured to accept POST requests. We do this by adding methods=['GET', 'POST'] to the @app.route() decorator. Inside the view function, we can then check if request.method == 'POST' to run specific logic only when a form is submitted.
2. The Professional Approach: Flask-WTF
While you can handle forms manually by accessing request.form and writing if statements for every validation rule, this approach is cumbersome, insecure, and difficult to maintain. The standard, professional way to manage forms in Flask is with the Flask-WTF extension, which integrates the powerful WTForms library.
Flask-WTF provides a clean, object-oriented way to:
- Define your forms as Python classes.
- Apply powerful, pre-built validators (e.g., for email formats, password confirmation).
- Protect your application against Cross-Site Request Forgery (CSRF) attacks.
- Render form fields easily in your templates.

Let's dive into a practical, step-by-step guide on using Flask-WTF to build registration and login forms. The following video by Corey Schafer is an excellent, comprehensive walkthrough. We will tackle it in sections.
3. Setting Up Your Application for Forms
First, we need to install Flask-WTF and configure our application with a secret key. This key is crucial for enabling Flask's session functionality and for Flask-WTF to generate secure CSRF tokens.
pip install Flask-WTF
In your main application file (e.g., app.py), you need to set the secret key:
from flask import Flask
app = Flask(__name__)
# The secret key should be a long, random string.
app.config['SECRET_KEY'] = 'a-very-long-and-random-secret-key'
# ... your routes go here ...
The video below will briefly cover this setup.
Python Flask Tutorial: Full-Featured Web App Part 3 - Forms and User Input
Let's start by seeing how to install Flask-WTF and configure the secret key.
Watch from 00:00 to 01:23 and from 10:19 to 11:54. These sections cover: The purpose of Flask-WTF and how to install it. The importance of the SECRET_KEY for security and how to set it in your app configuration.
4. Defining Form Classes
With Flask-WTF, you define the structure of your forms in a separate forms.py file. This keeps your code organized. Each form is a Python class that inherits from FlaskForm, and its fields are class attributes.
Key components you'll use:
- Fields:
StringField,PasswordField,BooleanField,SubmitField, etc. - Validators:
DataRequired(ensures the field is not empty),Length,Email,EqualTo(checks if two fields match, like a password and its confirmation).
Python Flask Tutorial: Full-Featured Web App Part 3 - Forms and User Input
Now, let's learn how to create the Python classes that represent our registration and login forms.
Watch from 02:03 to 10:19. This is the core of defining your forms. Pay close attention to: How to create a class like RegistrationForm(FlaskForm). How to define fields like username = StringField(...). How to attach a list of validators to each field: validators=[DataRequired(), Length(min=2, max=20)].
5. Rendering Forms and Handling Submissions
Once your form class is defined, you need to:
- Instantiate it in your view function and pass it to your template.
- Render it in your Jinja2 template using the provided form object.
- Process the data when the form is submitted and passes validation.
The form.validate_on_submit() method is the magic here. It automatically checks if the request is a POST and if all validator rules pass.
The process looks like this:
In app.py:
from flask import render_template, flash, redirect, url_for
from forms import RegistrationForm # Import your form class
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# This block runs ONLY on a successful POST request
flash(f'Account created for {form.username.data}!', 'success')
return redirect(url_for('home')) # Redirect to another page
# This runs on a GET request or if validation fails
return render_template('register.html', title='Register', form=form)
In register.html:
<form method="POST" action="">
{{ form.hidden_tag() }} <!-- IMPORTANT: CSRF protection -->
<fieldset>
<legend>Join Today</legend>
<div>
{{ form.username.label }}
{{ form.username }}
</div>
<!-- ... other fields ... -->
</fieldset>
<div>
{{ form.submit }}
</div>
</form>
The form.hidden_tag() is essential—it renders a hidden input containing the CSRF token that protects your application.
Let's watch how this is all put together.
Python Flask Tutorial: Full-Featured Web App Part 3 - Forms and User Input
This part of the tutorial connects the Python code to the HTML template. You'll see how to display the form and handle the submission logic.
Watch from 11:54 to 31:53. This long segment covers several key steps: Routes and Form Instantiation (11:54 - 14:04): Creating the /register and /login routes and passing form instances to the templates. Rendering in HTML (14:04 - 22:01): Writing the Jinja2 template code to display the form, including the crucial form.hidden_tag(). Handling Submission (22:52 - 24:51): Using form.validate_on_submit() in the view function. User Feedback with flash() (24:51 - 31:53): Using flashed messages to show a success message upon valid submission and redirecting the user.
6. Displaying Validation Errors
What happens when a user enters an invalid email or passwords that don't match? form.validate_on_submit() will return False. Flask-WTF automatically attaches a list of error messages to the field that failed validation.
You can access these errors in your template via form.field_name.errors. It's best practice to display these errors right next to the corresponding input field to give the user clear, immediate feedback.
<div>
{{ form.email.label }}
{% if form.email.errors %}
{{ form.email(class="is-invalid") }} <!-- Add a CSS class for styling -->
<div class="invalid-feedback">
{% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.email }}
{% endif %}
</div>
This final video segment will show you exactly how to implement this crucial piece of user experience.
Python Flask Tutorial: Full-Featured Web App Part 3 - Forms and User Input
Let's finish by learning how to display specific validation errors to the user, guiding them to correct their input.
Watch from 31:53 to 38:17. This section demonstrates how to modify the Jinja2 template to check for and display errors for each form field.
Conclusion
Congratulations! You can now build interactive web pages that securely accept and validate user input. This is a fundamental skill for creating everything from simple contact forms to complex user registration and login systems.
Key Takeaways:
- Web forms should be submitted using the
POSTHTTP method to keep data secure. - Flask-WTF is the industry-standard library for handling forms in Flask.
- A
SECRET_KEYis required to enable CSRF protection. - Forms are defined as Python classes inheriting from
FlaskForm, with fields and validators. - In templates,
{{ form.hidden_tag() }}is essential for security, and fields are rendered with{{ form.field_name }}. - The
form.validate_on_submit()method simplifies handling form submissions by checking the request method and running all validators at once. - Validation errors are accessible in the template via
form.field_name.errors, allowing you to provide clear user feedback.
In the next lesson, we will build on what you've learned today. Now that we have a registration and login form, we need to manage the user's logged-in state. You will learn to explain session-based authentication and the importance of password hashing, allowing you to build a complete and secure authentication system.