By Benji Asperheim | 2024-09-07Blog Thumbnail

Python Flask Web Framework Tutorial

In this Flask Python tutorial, you'll learn how to build a simple web application using the Flask framework and containerize it with Docker for easy deployment. Flask is a popular choice for Python web development due to its minimalist, flexible approach. This guide will walk you through setting up a Flask project, serving static files like CSS and JavaScript, and deploying it using Docker. By the end of this tutorial, you'll have a functional web server running Flask, fully containerized using Docker, making your app easy to deploy anywhere.

For more information on using Docker and Python to deploy a web app, check out our article on the HTTP web server in Python.

WARNING: The instructions in this tutorial are geared toward users running a UNIX-like (e.g. Linux and macOS) operating system. Some of the instructions in this article may need adjusting for Windows users. For example, the python command in Windows is typically aliased to py, and the mkdir (make directory) command is md in Windows Command Prompt.

DISCLAIMER: The introduction and conclusion were written by ChatGPT.

Flask Web Framework Tutorial

Brief Flask Framework in Python Overview

Flask is often preferred for web development in Python due to its simplicity, flexibility, and minimalism. It provides a lightweight framework, with a straightforward approach, that allows developers to add only the components they need, making it easy to start and scale projects.

Installing Flask

You can use pip3 install flask (or install it in a virtual environment), but in this article we will use a requirements.txt to install (a specific version of) Flask.

Make the requirements.txt (touch requirements.txt) file, in the root of your project directory, and place the following:

Flask==3.0.3

NOTE: Check the Flask project pypi.org page to get the latest version number. Add any additional packages to the requirements file as-needed.

Now you can install the packages listed in requirements by running the following pip install command in the root of your project:

pip3 install -r requirements.txt

The -r (or --requirement) flag will inform PIP that these are the required packages it must install.

Flask Example Written in Python

Make an app directory in the root of your project (mkdir app), and create a main.py (touch app/main.py) Python script, and the code in the script should look like this:

from flask import Flask, render_template

app = Flask("My Flask App")

# Serve the HTML template on the root route
@app.route('/')
def home():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

NOTE: The debug=True flag is used for development only. In production that should always be set to False.

Flask and HTML Templates

Make a templates directory (mkdir templates) for Flask, and create an HTML file inside of that templates directory (touch templates/index.html).

In the HTML file we use {{ url_for('static', filename='path') }} to link to static files dynamically.

Static CSS for the Template

We'll also need a styles.css file inside of the app's "static" directory. Create the /static directories with this command:

mkdir -p static/css && mkdir -p static/js

Now add the following to the CSS file:

body {
  font-family: Arial, sans-serif;
  background-color: #f0f0f0;
}

h1 {
  color: #333;
}

Flask JavaScript Example

Here's some example JavaScript we can use to test that JS is working properly in the Flask web app:

document.getElementById("js-button").addEventListener("click", function () {
  alert("Button clicked!");
});

Make sure to place the above code inside of static/js/script.js.

Running the Website Flask App

You've completed the coding part of the tutorial! Now let's go into the app/ directory (with cd app) and run the Flask web app like so:

python3 main.py

NOTE: If Flask returns an "Address already in use" error, then stop the server (with CTRL+c), and either kill the other process running on port 5000, or change the port= value, in main.py, to a different port number.

You should see the Flask framwork output the following:

 * Serving Flask app 'My Flask App'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5123
 * Running on http://192.168.18.143:5123
Press CTRL+C to quit
192.168.18.143 - - [07/Sep/2024 17:20:56] "GET / HTTP/1.1" 200 -
192.168.18.143 - - [07/Sep/2024 17:20:56] "GET /static/css/styles.css HTTP/1.1" 200 -
192.168.18.143 - - [07/Sep/2024 17:20:56] "GET /static/js/script.js HTTP/1.1" 200 -
192.168.18.143 - - [07/Sep/2024 17:20:56] "GET /favicon.ico HTTP/1.1" 404 -

Flask Docker Example

For the Dockerized Flask app we will need to add a Dockerfile and a docker-compose.yml file to the root of the project.

Flask Docker Project Layout

The layout (file structure) of the Python Flask app should look like this:

flask_app/
│
├── Dockerfile
├── docker-compose.yml
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── static/
│   │   ├── css/
│   │   │   └── styles.css
│   │   ├── js/
│   │   │   └── script.js
│   └── templates/
│       └── index.html
└── requirements.txt

Notice how most of the files, not related to Docker, go inside of the app/ directory, with the noticable exception being requirements.txt.

Flask Dockerfile Example

The Dockerfile should look like this:

FROM python:3.12-slim

# Set the working directory in the container
WORKDIR /app

# Copy the requirements file into the container at /app
COPY requirements.txt /app/

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy the app directory into the container
COPY app /app/

# Make port available to the world outside this container
EXPOSE 5003

# Run the Flask app
CMD ["python", "main.py"]

This will copy the requirements.txt file, that we made earlier, into the container, and install Flask (as well as any other Python packages) inside of the ephemeral container each time it is built and spun up, and it exposes the web app to port 5003 on the host machine.

Flask Docker-Compose File

The docker-compose.yml YAML should look like this:

services:
  python_flask_example:
    container_name: python_flask_example
    build: .
    ports:
      - "5003:5000"
    volumes:
      - ./app:/app # Bind-mount the app directory into the container
    environment:
      FLASK_ENV: development # Flask runs in development mode
    command: python main.py

NOTE: The version line for the Docker compose file is now deprecated.

We will also need to modify the main.py code somewhat, so that it can dynamically change the debug boolean based on the FLASK_ENV value passed by the compose file:

from flask import Flask, render_template
import os

# Create the Flask application instance
app = Flask("My Flask App")

# Serve the HTML template on the root route
@app.route('/')
def home():
    return render_template('index.html')

if __name__ == '__main__':
    # Get the FLASK_ENV environment variable
    flask_env = os.getenv('FLASK_ENV', 'production') # Default to 'production' if not set

    # Determine if debug mode should be enabled
    is_debug = True if 'dev' in flask_env.lower() else False

    # Run the Flask app with debug mode based on the environment variable
    app.run(host='0.0.0.0', port=5000, debug=is_debug)

NOTE: For production, you can just make a new docker-compose.prod.yml file (or something equivalent), and change the service to have an environment with something like FLASK_ENV: prod, and build the production container with docker compose -f docker-compose.prod.yml build.

Flask Docker Build Example

Once the Python code has been modified, and you have your Docker-related files setup correctly, just build and spin up the container like so:

docker compose build # or 'docker compose -f docker-compose.yml build'
docker compose up # or 'docker compose -f docker-compose.yml up'

You can also spin up the container, and rebuild as needed, with this command passing the --build flag:

docker compose up --build

Docker will output a bunch of information like so:

docker compose up --build
[+] Building 1.9s (11/11) FINISHED                                                                                                                                                             docker:desktop-linux
 => [python_flask_example internal] load build definition from Dockerfile                                                                                                                                      0.0s
 => => transferring dockerfile: 495B                                                                                                                                                ...
 ...                                                                                                                                                                        0.0s
 => => writing image sha256:7329e74cdc74b76f8bcd945068ad40cbadba966bea4f4b2447e3a199ba9734a9                                                                                                                   0.0s
 => => naming to docker.io/library/python-flask-docker-python_flask_example                                                                                                                                    0.0s
[+] Running 2/1
 ✔ Network python-flask-docker_default  Created                                                                                                                                                                0.0s
 ✔ Container python_flask_example       Created                                                                                                                                                                0.0s
Attaching to python_flask_example
python_flask_example  |  * Serving Flask app 'My Flask App'
python_flask_example  |  * Debug mode: on
python_flask_example  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
python_flask_example  |  * Running on all addresses (0.0.0.0)
python_flask_example  |  * Running on http://127.0.0.1:5000
python_flask_example  |  * Running on http://172.23.0.2:5000
python_flask_example  | Press CTRL+C to quit
python_flask_example  |  * Restarting with stat
python_flask_example  |  * Debugger is active!
python_flask_example  |  * Debugger PIN: 132-583-921
python_flask_example  | 192.168.65.1 - - [07/Sep/2024 10:20:49] "GET / HTTP/1.1" 200 -
python_flask_example  | 192.168.65.1 - - [07/Sep/2024 10:20:49] "GET /static/css/styles.css HTTP/1.1" 200 -
python_flask_example  | 192.168.65.1 - - [07/Sep/2024 10:20:49] "GET /static/js/script.js HTTP/1.1" 200 -
python_flask_example  | 192.168.65.1 - - [07/Sep/2024 10:20:49] "GET /favicon.ico HTTP/1.1" 404 -

Notice how the Flask app, running inside of the container, tells you that it's running on port 5000, but, in order to access the web app in a browser, you will need to navigate to http://localhost:5003 in a browser to view the web page. This is because we bind mounted and exposed the Flask app (running on port 5000) to port 5003 on the host machine.

Flask Web Framework Flask Python Flask Docker Screenshot

Conclusion

This Flask in Python tutorial provided a step-by-step guide to creating a simple web application, setting it up to serve static files, and deploying it using Docker. You learned how to structure your project, install Flask, and handle both development and production environments using Docker Compose. Flask's lightweight nature, combined with Docker's containerization, makes this setup ideal for developers looking for both flexibility and scalability. With these tools, you're well-equipped to build and deploy robust Python web applications.

Discover expert insights and tutorials on adaptive software development, Python, DevOps, creating website builders, and more at Learn Programming. Elevate your coding skills today!