Kahibaro
Discord Login Register

Handling user input (forms)

Understanding Form Handling in Flask

In web development, “handling user input” usually means:

  1. Showing a form in the browser (HTML).
  2. Letting the user type something and click a button.
  3. Receiving that data on the server (your Flask app).
  4. Doing something with it (validating, saving, responding).

This chapter focuses on how to do that with Flask.


The basic request cycle for forms

When you work with forms, two HTTP methods matter most:

Typical pattern:

  1. Browser sends GET request → server returns a page with a form.
  2. User fills the form and clicks “Submit”.
  3. Browser sends POST request with the form fields.
  4. Server (Flask) reads the submitted data and responds.

In Flask, you handle this with routes that accept specific methods.


Creating a simple HTML form in a Flask template

Assume you have a Flask app and templates set up (as covered earlier in the chapter on templates).

A basic HTML form in a template (for example templates/contact.html):

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Contact form</title>
  </head>
  <body>
    <h1>Contact us</h1>
    <form action="/contact" method="post">
      <label for="name">Your name:</label>
      <input type="text" id="name" name="name">
      <br>
      <label for="message">Message:</label>
      <textarea id="message" name="message"></textarea>
      <br>
      <button type="submit">Send</button>
    </form>
  </body>
</html>

Important parts:

Defining a route that handles form submission

On the Flask side, you create a route that accepts both GET and POST:

from flask import Flask, request, render_template
app = Flask(__name__)
@app.route("/contact", methods=["GET", "POST"])
def contact():
    if request.method == "POST":
        # The user submitted the form
        user_name = request.form.get("name")
        user_message = request.form.get("message")
        # Do something with the data (for now, just show a response)
        return f"Thanks, {user_name}! You wrote: {user_message}"
    # If it's a GET request, show the empty form
    return render_template("contact.html")

Key ideas:

Accessing form data with `request.form`

request.form works like a dictionary:

Common ways to access data:

name = request.form["name"]          # raises an error if 'name' is missing
email = request.form.get("email")    # returns None if 'email' is missing

Using .get() is safer for beginners because it won’t crash if the field is not present. You can even provide a default:

name = request.form.get("name", "Anonymous")

Handling multiple types of form fields

Different HTML input types are all accessed through request.form (or related objects). The field type is set in the HTML, but the way you read it in Flask is similar.

Text fields

HTML:

<input type="text" name="username">

Flask:

username = request.form.get("username")

Password fields

HTML:

<input type="password" name="password">

Flask:

password = request.form.get("password")

You treat it as a normal string, but in real apps you must handle it securely (hashed, never printed, etc.).

Textarea (multi-line text)

HTML:

<textarea name="bio"></textarea>

Flask:

bio = request.form.get("bio")

Radio buttons

HTML:

<p>Choose a color:</p>
<label>
  <input type="radio" name="color" value="red"> Red
</label>
<label>
  <input type="radio" name="color" value="blue"> Blue
</label>
<label>
  <input type="radio" name="color" value="green"> Green
</label>

Flask:

favorite_color = request.form.get("color")

Only one value is sent (the selected radio button).

Checkboxes

HTML:

<label>
  <input type="checkbox" name="subscribe" value="yes">
  Subscribe to newsletter
</label>

Flask:

subscribed = request.form.get("subscribe")  # 'yes' if checked, None if not

If you have multiple checkboxes with the same name:

HTML:

<label>
  <input type="checkbox" name="hobbies" value="music"> Music
</label>
<label>
  <input type="checkbox" name="hobbies" value="sports"> Sports
</label>
<label>
  <input type="checkbox" name="hobbies" value="reading"> Reading
</label>

Flask:

selected_hobbies = request.form.getlist("hobbies")
# e.g. ['music', 'reading']

Use .getlist() when you expect multiple values for the same field name.

Select (dropdown)

HTML:

<select name="country">
  <option value="us">United States</option>
  <option value="uk">United Kingdom</option>
  <option value="de">Germany</option>
</select>

Flask:

country = request.form.get("country")

It will be one of "us", "uk", "de".


Using POST–Redirect–GET to avoid resubmission

When a user submits a form and you return a page directly, refreshing the page can cause the browser to re-submit the form.

A common pattern is:

  1. Handle POST data.
  2. Do your processing.
  3. Redirect to another page (or to the same route with GET).

This is known as “POST–Redirect–GET”.

Example:

from flask import Flask, request, render_template, redirect, url_for
app = Flask(__name__)
messages = []
@app.route("/guestbook", methods=["GET", "POST"])
def guestbook():
    if request.method == "POST":
        name = request.form.get("name", "Anonymous")
        text = request.form.get("text", "")
        messages.append({"name": name, "text": text})
        # Redirect after handling POST
        return redirect(url_for("guestbook"))
    # GET request: show the form and messages
    return render_template("guestbook.html", messages=messages)

Template guestbook.html (simplified):

<!DOCTYPE html>
<html>
  <body>
    <h1>Guestbook</h1>
    <form action="{{ url_for('guestbook') }}" method="post">
      <input type="text" name="name" placeholder="Your name">
      <br>
      <textarea name="text" placeholder="Your message"></textarea>
      <br>
      <button type="submit">Sign</button>
    </form>
    <h2>Messages</h2>
    <ul>
      {% for msg in messages %}
        <li><strong>{{ msg.name }}:</strong> {{ msg.text }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

Here, after submitting, the user is redirected back to /guestbook with GET, so refreshing only reloads the list, not re-submits the form.


Basic validation of user input

Beginners often forget that users might:

Simple validation means checking the data before using it.

Example: a simple “add numbers” form.

Template add.html:

<!DOCTYPE html>
<html>
  <body>
    <h1>Add two numbers</h1>
    {% if error %}
      <p style="color: red;">{{ error }}</p>
    {% endif %}
    <form action="{{ url_for('add') }}" method="post">
      <input type="text" name="a" placeholder="First number">
      <input type="text" name="b" placeholder="Second number">
      <button type="submit">Add</button>
    </form>
    {% if result is not none %}
      <p>Result: {{ result }}</p>
    {% endif %}
  </body>
</html>

Flask route:

from flask import Flask, request, render_template
app = Flask(__name__)
@app.route("/add", methods=["GET", "POST"])
def add():
    error = None
    result = None
    if request.method == "POST":
        a_str = request.form.get("a", "").strip()
        b_str = request.form.get("b", "").strip()
        if not a_str or not b_str:
            error = "Please fill in both numbers."
        else:
            try:
                a = float(a_str)
                b = float(b_str)
                result = a + b
            except ValueError:
                error = "Both values must be numbers."
    return render_template("add.html", error=error, result=result)

Key ideas:

Showing submitted data back in the template

Often you want to show the values the user submitted, for example to confirm a submission.

In the route:

@app.route("/signup", methods=["GET", "POST"])
def signup():
    if request.method == "POST":
        username = request.form.get("username")
        email = request.form.get("email")
        return render_template("signup_done.html",
                               username=username, email=email)
    return render_template("signup_form.html")

In signup_done.html:

<!DOCTYPE html>
<html>
  <body>
    <h1>Signup complete</h1>
    <p>Welcome, {{ username }}!</p>
    <p>We will send a confirmation to {{ email }}.</p>
  </body>
</html>

Data flows:

  1. User submits form.
  2. Flask reads request.form.
  3. Flask passes values into render_template.
  4. Template displays them with {{ ... }}.

Keeping track of form errors and values

A simple pattern for more complex forms is to store everything in dictionaries.

Example:

@app.route("/profile", methods=["GET", "POST"])
def profile():
    errors = {}
    values = {"name": "", "age": ""}
    if request.method == "POST":
        values["name"] = request.form.get("name", "").strip()
        values["age"] = request.form.get("age", "").strip()
        if not values["name"]:
            errors["name"] = "Name is required."
        try:
            age = int(values["age"])
            if age <= 0:
                errors["age"] = "Age must be a positive number."
        except ValueError:
            errors["age"] = "Age must be an integer."
        if not errors:
            return f"Profile saved for {values['name']}, age {age}"
    return render_template("profile.html", errors=errors, values=values)

Template profile.html (simplified):

<!DOCTYPE html>
<html>
  <body>
    <h1>Edit profile</h1>
    <form method="post">
      <label>Name:
        <input type="text" name="name" value="{{ values.name }}">
      </label>
      {% if errors.name %}
        <span style="color:red">{{ errors.name }}</span>
      {% endif %}
      <br>
      <label>Age:
        <input type="text" name="age" value="{{ values.age }}">
      </label>
      {% if errors.age %}
        <span style="color:red">{{ errors.age }}</span>
      {% endif %}
      <br>
      <button type="submit">Save</button>
    </form>
  </body>
</html>

Here:

Handling query parameters (simple GET input)

Not all user input comes from forms with method="post". Sometimes data is passed in the URL, for example:

These are called query parameters and are accessed with request.args.

Example:

from flask import Flask, request
app = Flask(__name__)
@app.route("/search")
def search():
    query = request.args.get("q", "")
    return f"You searched for: {query}"

If the user visits /search?q=flask, then query is "flask".

Use this when:

Very brief note on security

Even in small practice apps, remember:

For this beginner course, you focus on the mechanics of receiving and using data, but keep in mind that security matters as you move to more serious projects.


Summary

In this chapter you saw how to:

These tools let you build interactive web pages where users can enter data and your Python code can respond to it.

Views: 15

Comments

Please login to add a comment.

Don't have an account? Register now!