Building a Simple To-Do List App with Flask

Introduction: Your First Step into Web Development!

Have you ever wanted to create your own web application but felt overwhelmed by all the complex terms and technologies? Well, you’re in luck! Today, we’re going to build a simple To-Do List app using a fantastic Python tool called Flask. This project is perfect for beginners because it covers many core concepts of web development without getting too complicated.

What is Flask?
Flask is a “micro” web framework for Python. Think of it as a small, lightweight toolkit that helps you build web applications quickly and efficiently. It provides the essential tools you need to get started, letting you choose other components as your app grows. Because it’s written in Python, it’s very easy to read and understand, making it an excellent choice for newcomers.

Why build a To-Do List app? It’s a classic introductory project for a reason! It allows us to explore how to:
* Display information on a web page.
* Accept input from users (like adding a new task).
* Store and retrieve data (so your tasks don’t disappear!).
* Make your app interactive (marking tasks as complete).

By the end of this guide, you’ll have a working To-Do List app and a solid foundation for your web development journey. Let’s get started!

Getting Ready: What You’ll Need

Before we dive into the code, let’s make sure your computer is set up correctly.

  • Python: Flask is a Python framework, so you’ll need Python installed on your system.
    • You can check if you have Python by opening your terminal or command prompt and typing:
      bash
      python3 --version

      or sometimes just:
      bash
      python --version
    • If you don’t have it, or you have an older version (we recommend Python 3.8+), you can download it from the official Python website: python.org/downloads.
  • pip: This is Python’s package installer, and it usually comes bundled with Python. We’ll use pip to install Flask and other libraries.
  • Virtual Environments: This is a super important concept!
    • What is a virtual environment? Imagine you’re working on multiple projects, and each project needs specific versions of libraries. Without a virtual environment, all these libraries would be installed globally on your system, which can lead to conflicts. A virtual environment creates an isolated space for each project, ensuring that its dependencies don’t interfere with others. It’s like giving each project its own little sandbox!

Setting Up Your Workspace

Let’s create a dedicated folder for our project and set up a virtual environment.

  1. Create a Project Directory:
    Open your terminal or command prompt and run these commands:
    bash
    mkdir flask-todo-app
    cd flask-todo-app

    This creates a folder named flask-todo-app and moves you into it.

  2. Create and Activate a Virtual Environment:
    Inside your flask-todo-app directory, run:
    bash
    python3 -m venv venv

    This command creates a new virtual environment named venv (you can name it anything, but venv is common).

    Now, activate it:
    * On macOS/Linux:
    bash
    source venv/bin/activate

    * On Windows (Command Prompt):
    bash
    venv\Scripts\activate.bat

    * On Windows (PowerShell):
    bash
    venv\Scripts\Activate.ps1

    You’ll know it’s activated because (venv) will appear at the beginning of your terminal prompt!

  3. Install Flask:
    With your virtual environment activated, install Flask using pip:
    bash
    pip install Flask

    This will download and install Flask and its necessary components into your virtual environment.

Your First Flask Application: The “Hello, World!” of Web

Let’s create a very basic Flask application to make sure everything is working correctly. This is often called a “Hello, World!” app.

  1. Create app.py:
    Inside your flask-todo-app directory, create a new file named app.py.

  2. Add the following code to app.py:
    “`python
    from flask import Flask

    Create a Flask application instance

    app = Flask(name)

    Define a route for the home page (‘/’)

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

    This part ensures the app runs when you execute the script directly

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

  3. Explanation of the code:

    • from flask import Flask: This line imports the Flask class from the flask library.
    • app = Flask(__name__): This creates an instance of the Flask application. __name__ tells Flask where to look for resources like templates.
    • @app.route('/'): This is a “decorator” (a special Python syntax). It tells Flask that the function immediately below it (hello_world) should be executed when someone visits the root URL (/) of your web application.
    • def hello_world(): return 'Hello, Flask To-Do App!': This defines the function that handles requests to the / route. It simply returns a string, which Flask then displays in the user’s web browser.
    • if __name__ == '__main__': app.run(debug=True): This standard Python idiom ensures that the app.run() command only executes when you run app.py directly (not when it’s imported as a module). debug=True is useful for development as it provides helpful error messages and automatically reloads the server when you make changes. Remember to set debug=False in a production environment for security.
  4. Run Your Application:
    In your terminal (with the virtual environment still activated), run:
    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
    • Debugger is active!
    • Debugger PIN: …
      ``
      Open your web browser and go to
      http://127.0.0.1:5000`. You should see “Hello, Flask To-Do App!” displayed. Congratulations, your first Flask app is running!

Making it a To-Do List: Storing and Displaying Tasks

A simple “Hello, World!” is nice, but we need a To-Do list! Let’s start by displaying some predefined tasks. To do this, we’ll use Flask’s templating engine, Jinja2.

  1. Create a templates Folder:
    Flask expects your HTML files (templates) to be in a specific folder named templates inside your project directory.
    bash
    mkdir templates

  2. Create index.html:
    Inside the templates folder, create a new file named index.html. Add the following HTML:
    “`html
    <!DOCTYPE html>




    My To-Do List


    My To-Do List

        <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 task in tasks %}
            <li class="{% if task.status == 'completed' %}completed{% endif %}">
                <span>{{ task.id }}. {{ task.task }}</span>
                <div class="action-buttons">
                    {% if task.status != 'completed' %}
                    <form action="/complete/{{ task.id }}" method="POST" style="display:inline;">
                        <button type="submit">Complete</button>
                    </form>
                    {% endif %}
                    <form action="/delete/{{ task.id }}" method="POST" style="display:inline;">
                        <button type="submit" class="delete">Delete</button>
                    </form>
                </div>
            </li>
            {% else %}
            <li>No tasks yet! Add one above.</li>
            {% endfor %}
        </ul>
    </div>
    



    “`

  3. Update app.py to use the template:
    Now, let’s modify app.py to use this index.html file and pass some sample tasks to it.
    “`python
    from flask import Flask, render_template, request, redirect, url_for
    import sqlite3 # To interact with a SQLite database

    app = Flask(name)

    — Database Setup —

    DATABASE = ‘database.db’

    def get_db_connection():
    # Connects to the SQLite database
    conn = sqlite3.connect(DATABASE)
    # Allows accessing columns by name instead of index
    conn.row_factory = sqlite3.Row
    return conn

    def init_db():
    # Initializes the database schema (creates the table if it doesn’t exist)
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute(”’
    CREATE TABLE IF NOT EXISTS tasks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    task TEXT NOT NULL,
    status TEXT DEFAULT ‘pending’
    )
    ”’)
    conn.commit()
    conn.close()

    Initialize the database when the app starts

    with app.app_context():
    init_db()

    — Routes —

    @app.route(‘/’)
    def index():
    conn = get_db_connection()
    # Fetch all tasks from the database
    tasks = conn.execute(‘SELECT * FROM tasks’).fetchall()
    conn.close()
    # Render the index.html template and pass the tasks list to it
    return render_template(‘index.html’, tasks=tasks)

    @app.route(‘/add’, methods=[‘POST’])
    def add_task():
    # Check if the request method is POST
    if request.method == ‘POST’:
    # Get the ‘task’ data from the form
    task_content = request.form[‘task’]
    if task_content: # Ensure the task content is not empty
    conn = get_db_connection()
    # Insert the new task into the database with a ‘pending’ status
    conn.execute(‘INSERT INTO tasks (task) VALUES (?)’, (task_content,))
    conn.commit()
    conn.close()
    # Redirect back to the home page after adding the task
    return redirect(url_for(‘index’))

    @app.route(‘/complete/‘, methods=[‘POST’])
    def complete_task(task_id):
    conn = get_db_connection()
    # Update the status of the specific task to ‘completed’
    conn.execute(‘UPDATE tasks SET status = ? WHERE id = ?’, (‘completed’, task_id))
    conn.commit()
    conn.close()
    return redirect(url_for(‘index’))

    @app.route(‘/delete/‘, methods=[‘POST’])
    def delete_task(task_id):
    conn = get_db_connection()
    # Delete the specific task from the database
    conn.execute(‘DELETE FROM tasks WHERE id = ?’, (task_id,))
    conn.commit()
    conn.close()
    return redirect(url_for(‘index’))

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

Understanding Templates with Jinja2

In index.html, you’ll notice some special syntax:
* {{ task.task }}: These double curly braces are used to display variables passed from your Flask application. Here, task.task refers to the task property of each task object.
* {% for task in tasks %}{% endfor %}: These curly braces with percent signs are used for control flow, like loops and conditional statements. This loop iterates over the tasks list that we pass from app.py and creates a list item (<li>) for each task.
* {% if task.status == 'completed' %}completed{% endif %}: This is a conditional statement that adds the completed CSS class if the task’s status is ‘completed’.

Storing Data Permanently: Introducing SQLite

Our previous “tasks” were hardcoded in Python. If you restart the app, any new tasks would disappear. To make our To-Do list truly useful, we need to store tasks permanently. This is where databases come in!

What is SQLite?
SQLite is a super lightweight, file-based database. Unlike larger databases that run as separate servers, SQLite stores your entire database in a single file on your disk (e.g., database.db). It’s perfect for small applications like ours, as it requires no complex setup. Python even has a built-in module for working with SQLite, sqlite3.

Database Initialization and Interaction

In the updated app.py, we’ve added functions to handle our database:
* DATABASE = 'database.db': This defines the name of our database file.
* get_db_connection(): This helper function creates a connection to our SQLite database. conn.row_factory = sqlite3.Row is important because it allows us to access data by column name (e.g., task['task']) instead of by index, making our code much more readable.
* init_db(): This function is responsible for creating our tasks table in the database if it doesn’t already exist.
* The SQL command CREATE TABLE IF NOT EXISTS tasks (...) defines our table.
* id INTEGER PRIMARY KEY AUTOINCREMENT: An ID column that automatically increments for each new task.
* task TEXT NOT NULL: A column to store the task description (text), which cannot be empty.
* status TEXT DEFAULT 'pending': A column to store the task’s status, defaulting to ‘pending’.
* with app.app_context(): init_db(): This ensures init_db() is called when the Flask application starts, setting up our database.

Adding, Completing, and Deleting Tasks

Now let’s look at the routes that handle user interactions:

  • @app.route('/add', methods=['POST']):

    • This route handles the form submission when you add a new task.
    • methods=['POST'] specifies that this route only responds to POST requests (used for submitting data).
    • request.form['task'] retrieves the data from the input field named task in our index.html form.
    • conn.execute('INSERT INTO tasks (task) VALUES (?)', (task_content,)): This is an SQL INSERT statement that adds the new task to our database. The ? is a placeholder for task_content to prevent SQL injection vulnerabilities.
    • redirect(url_for('index')): After adding the task, the user is redirected back to the home page, which then displays the updated list of tasks.
  • @app.route('/complete/<int:task_id>', methods=['POST']):

    • This route is called when you click the “Complete” button next to a task.
    • <int:task_id> is a “variable part” of the URL. Flask automatically captures the number after /complete/ and passes it as the task_id argument to our function.
    • conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('completed', task_id)): This SQL UPDATE statement changes the status of the specified task to ‘completed’.
  • @app.route('/delete/<int:task_id>', methods=['POST']):

    • Similar to the complete route, this handles deleting a task.
    • conn.execute('DELETE FROM tasks WHERE id = ?', (task_id,)): This SQL DELETE statement removes the task with the matching id from the database.

Running Your To-Do List App

  1. Make sure your app.py and templates/index.html files are saved with the code provided.
  2. Ensure your virtual environment is activated.
  3. In your terminal, navigate to your flask-todo-app directory.
  4. Run the application:
    bash
    python app.py
  5. Open your web browser and go to http://127.0.0.1:5000.

You should now see your To-Do List app! Try adding tasks, marking them as complete, and deleting them. If you close and restart the app, your tasks will still be there because they are saved in the database.db file.

Conclusion

Congratulations! You’ve successfully built a functional To-Do List web application using Flask. Along the way, you’ve learned about:

  • Setting up a Flask project and virtual environments.
  • Creating basic Flask routes and rendering HTML templates.
  • Handling form submissions with GET and POST requests.
  • Storing and retrieving data using a SQLite database.
  • Making your app interactive with add, complete, and delete functionalities.

This is a fantastic foundation! From here, you can explore many ways to enhance your app:
* Add more complex styling with CSS frameworks like Bootstrap.
* Implement user accounts and authentication.
* Add due dates or task priorities.
* Deploy your application to a live server.

Keep experimenting and building – the world of web development is vast and exciting!


Comments

Leave a Reply