Skip to main content

To-Do List App - Part 2

·1254 words·6 mins
This is the second part of our To-Do List application. In this post, we will be looking at the Flask framework that we will be using for our web applications.

In the previous post, we mentioned that we will be using the Flask framework. Flask is a lightweight but powerful framework for writing web applications. Another populair framework that you might come across is Django. Django provides much more functionality, but you have to do things the Django way. Flask contains the basics that we need, and if you need more functionality, it gives you the freedom to add exactly what you need by installing your favourite library.

Before we start building our To-Do List, we will have a look at how we can build a web application with Flask. The first thing to do is install Flask. I suggest that you use a virtual environment. I assume you know how to use one. By using a virtual environment, we can install our Python libraries like Flask inside our project, so we don’t mess up the global Python installation. Let’s make a directory for our project, create a virtual environment, and activate it. In Linux, it’s something like this (I’m using venv, but feel free to use something like conda or uv):

$ mkdir todolist
$ cd todolist
$ python3 -m venv venv
$ source ./venv/bin/activate

Now we can install our first library inside our virtual environment with pip. Let’s start with Flask:

$ pip install Flask

To show you how easy it is to write a web application, let’s start with the most basic one. We will begin by writing our first version of our app, but instead of the todolist functionality, we’ll first see how Flask works. Create a file todolist.py with the following content:

# todolist.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def homepage():
    return 'This is the homepage.'

That’s all you need! Let’s have a look at what we did:

  • First we import flask from the Flask library that we installed earlier.
  • We create an instance of Flask and give it a name; usually we just use the __name__ variable.
  • We define our first route by using the @app.route decorator from Flask; the route is what you will see in the url-bar of your browser and is used to determine which page you want to show. The / refers to the homepage (just the domain name of the website). This will become more clear once we add more routes.
  • The decorator is used on a function that you can name whatever you like. It should return the contents of the web page. For now, we just return a string.

Now, you can run your app with the following command:

$ flask --app todolist run

This will start a simple server for development. This is not how you should deploy your final app, but it’s good enough for development. The command will show you something like:

* Serving Flask app 'todolist'
* 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 http://127.0.0.1:5000
Press CTRL+C to quit

Now you can open the url http://127.0.0.1:5000 in your browser, and you should see the text ‘This is the homepage.’

Note: 127.0.0.1 is the IP address of your computer. You can also use http://localhost:5000 to visit your app. The 5000 is the port number. You can run multiple apps by using different ports. If this port is alreay in use, you will get an error message ‘Address already in use’. You can use another port with the --port option after run, for example $ flask --app todolist run --port 5001

You may have noticed the ‘Debug mode: off’ message. Although you shouldn’t use debug mode on production, during local development you can enable it by adding the --debug option:

$ flask --app todolist --debug run

In debug mode, flask will automatically reload your files when you change your code. Also, if something goes wrong, your browser view will switch to an interactive debugging, showing you more info about error.

Let’s add two more routes to our program, to show you how to use html and route parameters. Add the following code to todolist.py:

@app.route('/html')
def html():
    return '<h1>HTML</h1><p>This page uses HTML.</p>'

@app.route('/item/<itemname>')
def item(itemname):
    return 'You are watching item: ' + itemname

If you are running in debug mode, Flask should automatically update when you save your changes. Otherwise, stop Flask (ctrl-C) and start it again.

Now you can visit http://127.0.0.1:5000/html to view your HTML page. HTML works with tags, which come in pairs: an opening tag like <h1>, and a corresponding closing tag that adds a slash: </h1>. The h1 marks this as a header text; a second-level header would use <h2>Subtitle</h2>. The <p> tag marks the text as a paragraph.

Since we haven’t added any CSS styling, the page uses some standard styling. It won’t look that spectacular, but at least the title should have a bigger font than the paragraph.

Tags can be nested, for example if you want text to be both bold (<b>) and italics (<i>), you could use <b><i>I am very important</i></b>. Just make sure that they don’t overlap, so close the last tag before closing earlier open tags. So <b><i>wrong</b></i> is not allowed.

Note: A few tags don’t need to be closed, like <br> (line break). Alternatively, you can use the single tag <br/>. If you make any mistakes, browsers are pretty good at fixing them, and will still show your page.

The second example shows how to use route parameters. In the example, we have a route /item/<itemname>. The angle brackets indicate the itemname is a parameter, so you can replace it with any string in the url.

The decorated function item that handles this route now has a parameter. Flask will retrieve the value from the url, and pass it as a parameter to our function. In our example, we add this string to the respons.

If you now visit for example http://127.0.0.1/item/hello, you will see the word ‘hello’ in our page.

Note: The item route is actually a security risk. The browser will display whatever you use as itemname in the url and will interpret it as HTML code. This can include JavaScript code, which will then run on the computer of the visitor and can do all kinds of things. In general, you shouldn’t trust user input (like the itemname in the url) and just include it in your HTML page. Flask provides an escape function that can turn user input into strings that are safe to include. Instead of returning an HTML string, we will be using (Jinja) templates in our app later on. These templates will automatically escape dangerous input.

By default, Flask interprets the url parameters as strings. If we know the parameter will be an integer (e.g. an item id), we can change the function to:

@app.route('/item/<int:item_id>')
def item(item_id):
    return 'You are watching item: ' + str(item_id)

The int: tells Flask that we are expecting an integer. Visiting http://127.0.0.1/item/hello now gives an error, because ‘hello’ is not an integer. But http://127.0.0.1/item/42 still does work. Another difference is that the parameter to item() is now an integer, so we have to convert it to str before we can append it to our response text.

Now that we know the basics of Flask, it’s time to start working on our application. In the next section we will begin with our To-Do List app.