6.7. Forms - Part 2#

In this section, we’ll learn how to make HTML forms that send the form data using a HTTP POST request and how Flask handles such form submissions. We’ll also cover how to handle file uploads.

Forms that use POST requests send data in the body of the request and are used for forms that submit data for the web server to retain or file uploads.

Forms that use POST requests are commonly used for:

  • Login and registration pages

  • Adding or updating data (e.g., reviews, blog posts, comments)

6.7.1. Form Example#

Here’s a HTML form that collects a user’s name and email:

 1<!DOCTYPE html>
 2<html>
 3    <head>
 4        <title>Simple Form</title>
 5    </head>
 6    <body>
 7        <h1>Contact Form</h1>
 8        <form action="/submit" method="POST">
 9            <label for="name">Name:</label>
10            <input type="text" id="name" name="name"><br><br>
11
12            <label for="email">Email:</label>
13            <input type="email" id="email" name="email"><br><br>
14
15            <input type="submit" value="Submit">
16        </form>
17    </body>
18</html>

Explanation

  • Form Element

    • <form action="/submit" method="POST"> creates a form that submits data to the /submit URL using the POST method.

  • Input Fields

    • <input type="text" id="name" name="name"> creates a text input for the user’s name.

    • <input type="email" id="email" name="email"> creates an email input.

  • Submit Button

    • <input type="submit" value="Submit"> creates a button to send the form data to the server when clicked.

6.7.2. Handling Form Data#

Here’s how to handle the form data in Flask:

from flask import Flask, request

app = Flask(__name__)

@app.route('/submit', methods=['POST'])
def submit_form():
    name = request.form['name']
    email = request.form['email']
    return f"Name: {name}, Email: {email}"

app.run(debug=True), port=5000)

Explanation

  • Route Definition

    • The route /submit listens for POST requests. We specify this with methods=['POST'] so Flask knows this endpoint will handle form submissions.

  • Accessing Form Data

    • request.form['name'] and request.form['email'] access the submitted form data. The request.form dictionary contains the data sent by the form.

  • Returning a Response

    • The form data is displayed back to the user by returning a string with the name and email values.

6.7.3. Example: New Movie Review#

Let’s build a form for adding a new movie review to our “Movie Reviews” website. We’ll create a form page to submit data such as the movie title, release year, genre, review score, and review text. The review date will be automatically set.

Importantly we will need two route functions in our Flask app for the form:

  • the first to return the form HTML

  • the second to process the form data

Project structure:

├── app.py
├── movies.db
└── templates/
    └── index.html
    └── new_review.html
app.py#
 1from flask import Flask, request, redirect, url_for
 2from sqlalchemy import create_engine, text
 3from datetime import datetime
 4
 5app = Flask(__name__)
 6
 7# Connect to the database
 8engine = create_engine('sqlite:///movies.db')
 9
10@app.route('/add_review', methods=['GET'])
11def show_form():
12    return render_template('add_review.html')
13
14@app.route('/add_review', methods=['POST'])
15def add_review():
16    # Get data from the form
17    title = request.form['title']
18    release_year = request.form['release_year']
19    genre = request.form['genre']
20    review_score = request.form['review_score']
21    review_text = request.form['review_text']
22
23    review_date = datetime.now().strftime("%Y-%m-%d")
24
25    # Insert the review into the "database"
26    insert_statement = f'''
27        INSERT INTO reviews (title, release_year, genre, review_date, review_score, review_text)
28        VALUES ('{}', {}, '{}', {}, {}, '{}');
29    '''.format(title, release_year, genre, review_date, review_score, review_text)
30
31    # Execute the SQL query
32    connection.execute(text(insert_statement))
33
34    # Redirect to the form page
35    return redirect(url_for('add_review'))
36
37app.run(debug=True, port=5000)
new_review.html#
 1<!DOCTYPE html>
 2<html>
 3    <head>
 4        <title>Add Movie Review</title>
 5    </head>
 6    <body>
 7        <h1>Add a New Movie Review</h1>
 8        <form action="/add_review" method="POST">
 9            <label for="title">Movie Title:</label>
10            <input type="text" id="title" name="title"><br><br>
11
12            <label for="release_year">Release Year:</label>
13            <input type="number" id="release_year" name="release_year"><br><br>
14
15            <label for="genre">Genre:</label>
16            <input type="text" id="genre" name="genre"><br><br>
17
18            <label for="review_score">Review Score (1-10):</label>
19            <input type="number" id="review_score" name="review_score"><br><br>
20
21            <label for="review_text">Review Text:</label><br>
22            <textarea id="review_text" name="review_text"></textarea><br><br>
23
24            <input type="submit" value="Submit Review">
25        </form>
26    </body>
27</html>

Explanation

  • HTML Form

    • The form collects details like the movie title, release year, genre, score, and review text.

    • When the form is submitted, it sends a POST request to /add_review.

  • Flask Handling

    • The add_review() function receives the form data, extracts it using request.form, and adds the review to database.

  • Redirecting

    • After the review is added, the user is redirected back to the form page to indicate success.

6.7.4. HTML Forms - Uploading Files#

Here’s an example of an HTML form that allows users to upload files:

<form action="/upload" method="POST" enctype="multipart/form-data">
    <label for="file">Choose a file:</label>
    <input type="file" id="file" name="file">
    <input type="submit" value="Upload">
</form>

Explanation:

  • The enctype="multipart/form-data" attribute is required for forms that handle file uploads.

  • The <input type="file"> element allows the user to choose a file to upload.

6.7.5. Handling File Data#

When a file is uploaded, Flask uses the request.files dictionary to access the file. Flask also allows you to save the file to the server.

from flask import Flask, request

app = Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return "No file part"

    file = request.files['file']

    if file.filename == '':
        return "No selected file"

    # Save the file to the uploads directory
    file.save(f'uploads/{file.filename}')

    return f"File {file.filename} uploaded successfully!"

app.run(debug=True, port=5000)

Explanation

  • request.files['file'] retrieves the uploaded file.

  • file.save() saves the file to a specified directory on the server.

6.7.6. Example: Image Uploads#

To allow users to upload an image along with their movie review, we need to modify both the form and the Flask code.

Project structure:

├── app.py
├── movies.db
└── templates/
    └── index.html
    └── new_review.html
└── uploads/
app.py#
 1from flask import Flask, request, redirect, url_for
 2from sqlalchemy import create_engine, text
 3from datetime import datetime
 4import os
 5
 6app = Flask(__name__)
 7
 8UPLOAD_FOLDER = 'uploads/'
 9app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
10
11movie_reviews = []
12
13@app.route('/add_review', methods=['POST'])
14def add_review():
15    # Get form data
16    title = request.form['title']
17    release_year = request.form['release_year']
18    genre = request.form['genre']
19    review_score = request.form['review_score']
20    review_text = request.form['review_text']
21
22    review_date = datetime.now().strftime("%Y-%m-%d")
23
24    # Handle file upload
25    if 'image' in request.files:
26        image = request.files['image']
27        if image.filename != '':
28            image_path = os.path.join(app.config['UPLOAD_FOLDER'], image.filename)
29            image.save(image_path)
30        else:
31            image_path = None
32    else:
33        image_path = None
34
35    # Insert the review into the "database"
36    insert_statement = f'''
37        INSERT INTO reviews (title, release_year, genre, review_date, review_score, review_text, image_path)
38        VALUES ('{}', {}, '{}', {}, {}, '{}', '{}');
39    '''.format(title, release_year, genre, review_date, review_score, review_text, image_path)
40
41    # Execute the SQL query
42    connection.execute(text(insert_statement))
43
44    return redirect(url_for('add_review'))
45
46
47@app.route('/add_review', methods=['GET'])
48def show_form():
49    return render_template('add_review.html')
50
51app.run(debug=True, port=5000)
new_review.html#
 1<!DOCTYPE html>
 2<html>
 3    <head>
 4        <title>Add Movie Review</title>
 5    </head>
 6    <body>
 7        <h1>Add a New Movie Review</h1>
 8        <form action="/add_review" method="POST" enctype="multipart/form-data">
 9            <label for="title">Movie Title:</label>
10            <input type="text" id="title" name="title"><br><br>
11
12            <label for="release_year">Release Year:</label>
13            <input type="number" id="release_year" name="release_year"><br><br>
14
15            <label for="genre">Genre:</label>
16            <input type="text" id="genre" name="genre"><br><br>
17
18            <label for="review_score">Review Score (1-10):</label>
19            <input type="number" id="review_score" name="review_score"><br><br>
20
21            <label for="review_text">Review Text:</label><br>
22            <textarea id="review_text" name="review_text"></textarea><br><br>
23
24            <!-- New file upload field -->
25            <label for="image">Upload Poster Image:</label>
26            <input type="file" id="image" name="image"><br><br>
27
28            <input type="submit" value="Submit Review">
29        </form>
30    </body>
31</html>

Explanation

  • The form now includes a file input field for uploading a movie poster.

  • The add_review() function checks for the image, saves it in the uploads/ folder, and stores the file path in the review data.

To store the image path in the database, the reviews table should have an additional column, which can be achieved with the following SQL:

ALTER TABLE reviews ADD COLUMN poster_image_path TEXT;

You can download a version of the database with this change movies.db.