Developing a Simple To-Do List App with Flask for Beginners

Hello future web developers! Are you ready to dive into the exciting world of web development? Building a To-Do List application is a classic way to start, as it touches on many fundamental concepts you’ll use in bigger projects. In this guide, we’ll create a basic To-Do List app using Flask, a super friendly and beginner-friendly web framework for Python.

What is Flask?

Flask (pronounced “flah-sk”) is what we call a “micro-framework” for building web applications using Python.
* Micro-framework: This means it’s lightweight and doesn’t include many built-in tools or libraries by default. Instead, it gives you the essentials and lets you choose the extra components you need. This makes it very flexible and easy to get started with, especially for smaller projects or for learning the ropes of web development.
* Python: It’s built with Python, which is known for its readability and simplicity, making it a great choice for beginners.

Flask allows you to handle web requests, manage URLs (known as “routing”), render HTML pages to display information to users, and process data submitted through forms. It’s an excellent choice for learning the core concepts of web development without getting overwhelmed.

Why Build a To-Do List App?

A To-Do List app is perfect for beginners because it involves:
* Displaying data: Showing your list of tasks.
* Adding data: Creating new tasks.
* Updating/Deleting data: Marking tasks as done or removing them.
* Handling user input: Using forms to add or delete tasks.
* Structuring a basic web application: Understanding how different parts (Python code, HTML pages) connect.

By the end of this guide, you’ll have a functional To-Do List app running on your computer, and a better understanding of how web applications work!

What You’ll Need

Before we begin, make sure you have the following installed on your computer:

  • Python 3: Flask is a Python framework, so you’ll need Python installed. You can download it from the official Python website (python.org).
  • A text editor or IDE: Something like VS Code, Sublime Text, or PyCharm Community Edition to write your code.

That’s it! Let’s get started.

Step 1: Setting Up Your Workspace

First, we need to create a project folder and set up a virtual environment.

What is a Virtual Environment?

A virtual environment is like a separate, isolated workspace for your Python projects. It allows you to install specific versions of libraries for one project without affecting other projects or your main Python installation. This prevents conflicts and keeps your project dependencies organized. It’s a best practice in Python development.

  1. Create a Project Folder:
    Open your terminal or command prompt and create a new folder for our project:

    bash
    mkdir flask_todo_app
    cd flask_todo_app

  2. Create a Virtual Environment:
    Inside your flask_todo_app folder, run the following command to create a virtual environment (we’ll name it venv):

    bash
    python -m venv venv

  3. Activate the Virtual Environment:
    You need to activate the virtual environment so that any packages you install only apply to this project.

    • On macOS/Linux:
      bash
      source venv/bin/activate
    • On Windows (Command Prompt):
      bash
      venv\Scripts\activate.bat
    • On Windows (PowerShell):
      powershell
      .\venv\Scripts\Activate.ps1

    You’ll know it’s active when you see (venv) at the beginning of your terminal prompt.

Installing Flask

Now that your virtual environment is active, let’s install Flask using pip.

pip is Python’s package installer. It’s used to install and manage software packages written in Python, like Flask.

pip install Flask

Step 2: Building Your First Flask App (Hello World)

Let’s create a simple “Hello, World!” Flask application to make sure everything is working.

  1. Create app.py:
    Inside your flask_todo_app folder, create a new file named app.py. This will be the main file for our application.

  2. Add Basic Flask Code:
    Open app.py in your text editor and add the following code:

    “`python
    from flask import Flask

    app = Flask(name)

    @app.route(‘/’)
    def hello():
    return “Hello, Flask To-Do App!”

    if name == ‘main‘:
    app.run(debug=True)
    “`

    • from flask import Flask: This line imports the Flask class, which is the core of our web application.
    • app = Flask(__name__): This creates an instance of the Flask application. __name__ is a special Python variable that tells Flask where to look for resources like templates and static files.
    • @app.route('/'): This is a decorator that associates the hello function with the root URL (/) of our application. When someone visits http://127.0.0.1:5000/, this function will be called. This concept is called routing.
    • def hello(): return "Hello, Flask To-Do App!": This function simply returns a string. Flask automatically sends this string back to the user’s browser as the response.
    • if __name__ == '__main__': app.run(debug=True): This ensures that the Flask development server runs only when app.py is executed directly. debug=True is useful during development because it automatically reloads the server when you make changes and provides helpful error messages. Remember to set debug=False in production!
  3. Run Your App:
    Save app.py and go back to your terminal (with the virtual environment activated). Run your app using:

    bash
    python app.py

    You should see output similar to this:

    * Serving Flask app 'app'
    * Debug mode: on
    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
    * Restarting with stat

    Open your web browser and go to http://127.0.0.1:5000. You should see “Hello, Flask To-Do App!”. Congratulations, your first Flask app is running!

Step 3: Introducing HTML Templates

Displaying plain text is boring. Let’s make our To-Do app look a bit nicer using HTML. Flask uses a templating engine called Jinja2 to render dynamic HTML pages.

Templates are essentially HTML files with special placeholders that Flask (or Jinja2) fills with data from your Python code.

  1. Create a templates Folder:
    Flask expects your HTML templates to be in a folder named templates inside your project directory. Create this folder:

    bash
    mkdir templates

  2. Create index.html:
    Inside the templates folder, create a new file called index.html.

  3. Add Basic HTML to index.html:
    html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Flask To-Do App</title>
    <style>
    body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f4; }
    h1 { color: #333; }
    ul { list-style-type: none; padding: 0; }
    li { background-color: #fff; border: 1px solid #ddd; padding: 10px; margin-bottom: 5px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; }
    form { display: inline; }
    input[type="text"] { padding: 8px; border: 1px solid #ccc; border-radius: 4px; width: 300px; }
    button { background-color: #4CAF50; color: white; padding: 8px 12px; border: none; border-radius: 4px; cursor: pointer; }
    button:hover { background-color: #45a049; }
    .delete-button { background-color: #f44336; }
    .delete-button:hover { background-color: #da190b; }
    </style>
    </head>
    <body>
    <h1>My To-Do List</h1>
    <form action="/add" method="post">
    <input type="text" name="task" placeholder="Add a new task" required>
    <button type="submit">Add Task</button>
    </form>
    <ul>
    <!-- To-Dos will go here -->
    </ul>
    </body>
    </html>

    (I’ve included some basic CSS for better readability, even though it’s not strictly part of the Flask logic.)

  4. Update app.py to Render the Template:
    We need to import render_template from Flask and modify our hello function.

    “`python
    from flask import Flask, render_template # <– Import render_template

    app = Flask(name)

    @app.route(‘/’)
    def index(): # Renamed the function to ‘index’ for clarity
    return render_template(‘index.html’) # <– Render our HTML file

    if name == ‘main‘:
    app.run(debug=True)
    “`

    Save app.py. If your debug server is running, it should auto-reload. Refresh your browser at http://127.0.0.1:5000. You should now see a page with “My To-Do List” and an input field.

Step 4: Creating the To-Do List Core Logic

For simplicity, we’ll store our to-do items in a simple Python list in memory. This means the list will reset every time you restart the server. Later, we’ll discuss how to make it permanent using a database.

  1. Add a List for Tasks in app.py:
    “`python
    from flask import Flask, render_template, request, redirect, url_for # <– Added request, redirect, url_for

    app = Flask(name)

    Our list to store to-do items. Each item is a dictionary.

    For now, we’ll just store the task description.

    todos = []
    current_id = 1 # To give each task a unique ID

    @app.route(‘/’)
    def index():
    # Pass the todos list to our HTML template
    return render_template(‘index.html’, todos=todos) # <– Pass todos list

    … (Other routes will go here)

    if name == ‘main‘:
    app.run(debug=True)
    “`

    • todos = []: This is our in-memory storage for tasks.
    • current_id = 1: We’ll use this to assign unique IDs to our tasks.
    • return render_template('index.html', todos=todos): We’re now passing the todos list to our index.html template. Inside the template, we can access this list using the variable name todos.
  2. Displaying To-Dos in index.html:
    Now, let’s update index.html to loop through the todos list and display each item. Jinja2 templates use special syntax for this.

    html
    <!-- ... inside <body> ... -->
    <h1>My To-Do List</h1>
    <form action="/add" method="post">
    <input type="text" name="task" placeholder="Add a new task" required>
    <button type="submit">Add Task</button>
    </form>
    <ul>
    {% for todo in todos %}
    <li>
    <span>{{ todo.task }}</span>
    <form action="/delete/{{ todo.id }}" method="post" style="margin-left: 10px;">
    <button type="submit" class="delete-button">Delete</button>
    </form>
    </li>
    {% else %}
    <li>No tasks yet! Add one above.</li>
    {% endfor %}
    </ul>
    </body>
    </html>

    * {% for todo in todos %}: This is a Jinja2 loop that iterates over the todos list that we passed from app.py.
    * <li><span>{{ todo.task }}</span></li>: For each todo item in the list, we display its task property using {{ todo.task }}. The double curly braces {{ }} are used to display variables.
    * {% else %}: This block runs if the todos list is empty.
    * {% endfor %}: Closes the loop.

    At this point, if you refresh your browser, you’ll still see “No tasks yet!” because our todos list is empty.

Step 5: Adding New To-Dos

Now let’s add the functionality to submit a new task through the form.

HTTP Methods (GET/POST):
* GET: Used to request data from a specified resource (e.g., when you type a URL in your browser).
* POST: Used to send data to a server to create/update a resource (e.g., submitting a form).

Our form in index.html uses method="post" and action="/add", so we need a new route in app.py to handle POST requests to /add.

  1. Add @app.route('/add', methods=['POST']) in app.py:

    “`python

    … (imports and todos list)

    @app.route(‘/’)
    def index():
    return render_template(‘index.html’, todos=todos)

    @app.route(‘/add’, methods=[‘POST’])
    def add():
    global current_id # Declare current_id as global to modify it
    task_description = request.form[‘task’] # Get the ‘task’ input from the form

    # Add the new task to our list with a unique ID
    todos.append({'id': current_id, 'task': task_description})
    current_id += 1 # Increment for the next task
    
    return redirect(url_for('index')) # Redirect back to the main page
    

    … (if name == ‘main‘:)

    ``
    *
    @app.route(‘/add’, methods=[‘POST’]): This decorator tells Flask that thisaddfunction should handle requests to the/addURL, but *only* if they are POST requests.
    *
    request.form[‘task’]: Therequestobject (imported fromflask) contains all incoming request data.request.formis a dictionary-like object that holds data from HTML forms submitted withmethod=”post”. We access the input field namedtask.
    *
    todos.append(…): We add a new dictionary representing the task to ourtodoslist.
    *
    redirect(url_for(‘index’)): After processing the form, it's good practice to redirect the user back to the main page (index).url_for(‘index’)generates the URL for theindexfunction (which is/`). This prevents issues if the user refreshes the page after submitting a form (a “PRG pattern” – Post/Redirect/Get).

    Save app.py. Now, go to http://127.0.0.1:5000, type a task in the input field, and click “Add Task.” You should see your task appear in the list!

Step 6: Deleting To-Dos

Finally, let’s add functionality to delete tasks. We’ll use another form for this, as it’s a robust way to handle deletions (especially for beginners).

In index.html, we already have a delete form for each task:
<form action="/delete/{{ todo.id }}" method="post" ...>

This means we need a route in app.py that can handle POST requests to a dynamic URL like /delete/1, /delete/2, etc.

  1. Add @app.route('/delete/<int:todo_id>', methods=['POST']) in app.py:

    “`python

    … (imports, todos list, index and add routes)

    @app.route(‘/delete/‘, methods=[‘POST’])
    def delete(todo_id):
    global todos # Declare todos as global to modify it
    # Filter out the todo item with the matching id
    todos[:] = [todo for todo in todos if todo[‘id’] != todo_id]
    return redirect(url_for(‘index’))

    if name == ‘main‘:
    app.run(debug=True)
    ``
    *
    @app.route(‘/delete/‘, methods=[‘POST’]):
    *
    : This is a **variable part** of the URL. Flask will capture the number after/delete/and pass it as an integer (int) to thedeletefunction as thetodo_idargument.
    *
    methods=[‘POST’]: Ensures only POST requests trigger this function.
    *
    todos[:] = [todo for todo in todos if todo[‘id’] != todo_id]: This line creates a new list containing all tasks *except* the one whoseidmatchestodo_id. Thetodos[:] = …syntax efficiently replaces the content of thetodos` list in place.

    Save app.py. Now, you can add tasks and click the “Delete” button next to any task to remove it from the list.

Running Your To-Do App

You’ve built a functional Flask To-Do List app! To run it:

  1. Ensure your virtual environment is active.
  2. Navigate to your flask_todo_app directory in the terminal.
  3. Run python app.py.
  4. Open your browser to http://127.0.0.1:5000.

What’s Next? (Ideas for Improvement)

This simple app is a great starting point, but real-world applications often need more features:

  • Persistence (Databases): Our current app loses all data when the server restarts. To fix this, you would use a database (like SQLite, PostgreSQL, or MySQL) to store your tasks permanently. Flask integrates well with SQLAlchemy (an ORM – Object Relational Mapper that helps you interact with databases using Python objects instead of raw SQL queries).
  • Marking Tasks as Done: You could add a checkbox or a “Mark Done” button and store a completed status (e.g., {'id': 1, 'task': 'Buy groceries', 'completed': False}) for each task.
  • Styling: Use external CSS files (and potentially JavaScript) to make your app look much more polished and interactive. Flask can serve static files (like CSS and JS) from a static folder.
  • User Accounts: If multiple users need their own to-do lists, you’d implement user authentication and authorization.
  • Error Handling: Make your app more robust by handling cases where things go wrong (e.g., what if a task description is too long?).

Conclusion

Congratulations! You’ve just developed a basic To-Do List web application using Flask. You’ve learned about setting up a project, virtual environments, routing, rendering HTML templates, handling form submissions, and managing data in a simple in-memory list. This is a solid foundation for building more complex and interactive web applications in the future. Keep exploring, keep building, and happy coding!


Comments

Leave a Reply