Fun with Flask: Building a Simple To-Do List App

Hello there, aspiring developers and productivity enthusiasts! Ever wanted to build your own web application but felt overwhelmed by complex frameworks? Today, we’re going to dive into the wonderful world of Flask, a super lightweight and easy-to-use web framework for Python. We’ll build a simple To-Do List application, a perfect project to get your feet wet with web development.

This guide is designed for beginners, so don’t worry if you’re new to some of these concepts. We’ll break down everything step-by-step!

What is Flask?

Imagine you want to build a house. Some frameworks are like a massive construction company that provides everything from the foundation to the roof, often with pre-built rooms and specific ways of doing things. Flask, on the other hand, is like getting a very sturdy toolbox with all the essential tools you need to build your house, but you have the freedom to design and build it exactly how you want. It’s a “microframework” because it doesn’t try to do everything for you, giving you flexibility and making it easier to understand how things work under the hood.

We’re going to use Flask to create a simple web app that lets you:
* See a list of your To-Do items.
* Add new To-Do items.
* Mark items as complete.
* Delete items.

Sounds fun, right? Let’s get started!

Prerequisites

Before we jump into coding, make sure you have these things ready:

  • Python: You’ll need Python installed on your computer. We recommend Python 3.x. You can download it from the official Python website.
  • A Text Editor: Any text editor will do, like VS Code, Sublime Text, Atom, or even Notepad++. VS Code is a popular choice among developers.
  • Terminal or Command Prompt: This is where we’ll run commands to set up our project and start our Flask app.

Setting Up Your Environment

Good practice in Python development involves using something called a “virtual environment.”

What is a Virtual Environment?

A virtual environment is like a segregated container for your Python projects. Imagine you’re working on multiple projects, and each project needs different versions of libraries (special tools or code modules). Without a virtual environment, all these libraries would get installed globally on your system, potentially causing conflicts. A virtual environment keeps each project’s dependencies separate and tidy, preventing such headaches.

Let’s create one!

  1. Create a Project Directory:
    First, let’s make a folder for our project. Open your terminal or command prompt and type:

    bash
    mkdir flask_todo_app
    cd flask_todo_app

    This creates a new folder named flask_todo_app and then moves you into that folder.

  2. Create a Virtual Environment:
    Inside your flask_todo_app folder, run this command:

    bash
    python -m venv venv

    This command uses Python’s built-in venv module to create a new virtual environment named venv inside your project folder.

  3. Activate the Virtual Environment:
    Now, we need to “activate” it. This tells your system to use the Python and libraries from this specific virtual environment, not the global ones.

    • On macOS/Linux:
      bash
      source venv/bin/activate

    • On Windows (Command Prompt):
      bash
      venv\Scripts\activate

    • 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.

  4. Install Flask:
    With your virtual environment active, let’s install Flask.

    bash
    pip install Flask

    pip is Python’s package installer, used to install external libraries like Flask.

Our First Flask App (Hello, Flask!)

Let’s create a very simple Flask application to ensure everything is set up correctly.

  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 Flask application.

  2. Write the “Hello, Flask!” Code:
    Open app.py in your text editor and paste the following code:

    “`python
    from flask import Flask

    Create a Flask application instance

    name tells Flask where to look for resources like templates

    app = Flask(name)

    This is a “route” decorator.

    It tells Flask what URL should trigger our ‘hello_world’ function.

    @app.route(‘/’)
    def hello_world():
    return “Hello, Flask! This is our To-Do List app.”

    This block ensures the app only runs when this script is executed directly.

    if name == ‘main‘:
    app.run(debug=True) # debug=True allows for automatic reloading on code changes
    “`

    Quick Explanations:

    • from flask import Flask: This line imports the Flask class (a blueprint for creating Flask applications) from the flask library.
    • app = Flask(__name__): This creates an instance of our Flask application. __name__ is a special Python variable that represents the current module’s name. It helps Flask know where to find template files and static files later.
    • @app.route('/'): This is a “decorator.” It’s a special Python syntax that modifies the function below it. In Flask, @app.route('/') tells our application that whenever a user visits the root URL (/) of our website, the hello_world function should be executed.
    • def hello_world():: This is a standard Python function that gets called when the / route is accessed.
    • return "Hello, Flask! ...": This function returns a simple string, which Flask then sends back to the user’s browser.
    • if __name__ == '__main__':: This is a standard Python idiom. It ensures that the code inside this block (in our case, app.run()) only runs when app.py is executed directly, not when it’s imported as a module into another script.
    • app.run(debug=True): This starts the Flask development server. debug=True is useful during development because it automatically reloads the server when you make changes to your code, and it provides helpful debugging information if errors occur. Remember to turn debug=False for production applications!
  3. Run Your Flask App:
    Save app.py and go back to your terminal (making sure your virtual environment is still active). Run the app:

    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 (or click on the URL shown in your terminal). You should see “Hello, Flask! This is our To-Do List app.”

    Congratulations! Your first Flask app is running! Press CTRL+C in your terminal to stop the server.

Building the To-Do List Core

Now that we have a basic Flask app, let’s build out the To-Do list functionality. For simplicity, we’ll store our to-do items directly in a Python list for now. This means your to-do list will reset every time you stop and start the server, but it’s great for learning the basics. Later, you can upgrade to a database!

1. HTML Templates

Web applications typically separate their logic (Python code) from their presentation (HTML). Flask uses a “templating engine” called Jinja2 for this.

What is a Templating Engine?

A templating engine allows you to create dynamic HTML pages. Instead of just sending static HTML, you can embed special placeholders in your HTML files that Flask fills with data from your Python code.

  1. Create a templates Folder:
    Flask expects your HTML template files to be in a specific 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 named index.html. This file will display our to-do list and provide forms to add/manage tasks.

    Paste the following HTML into templates/index.html:

    “`html
    <!DOCTYPE html>




    My Simple To-Do App


    My To-Do List

        <form action="{{ url_for('add_task') }}" method="post">
            <input type="text" name="task" placeholder="Add a new to-do..." required>
            <input type="submit" value="Add Task">
        </form>
    
        <ul>
            {% for task in tasks %}
            <li class="{{ 'completed' if task.completed else '' }}">
                <span>{{ task.id }}. {{ task.text }}</span>
                <div class="actions">
                    {% if not task.completed %}
                    <form action="{{ url_for('complete_task', task_id=task.id) }}" method="post" style="display:inline;">
                        <button type="submit">Complete</button>
                    </form>
                    {% endif %}
                    <form action="{{ url_for('delete_task', task_id=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>
    



    “`

    Quick Explanations for Jinja2 in HTML:

    • {{ variable }}: This is how you display data passed from your Flask app. For example, {{ task.text }} will show the text of a task.
    • {% for item in list %}{% endfor %}: This is a “for loop” to iterate over a list of items (like our tasks).
    • {% if condition %}{% endif %}: This is an “if statement” to show content conditionally.
    • {{ url_for('function_name') }}: This is a very useful Jinja2 function. It generates the correct URL for a Flask route function. This is better than hardcoding URLs because if you change a route’s name, url_for will automatically update, preventing broken links.

2. Python Logic (app.py)

Now, let’s update app.py to handle our to-do list items, render our index.html template, and process user actions.

Replace the content of your app.py file with the following:

from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)

tasks = []
next_task_id = 1

@app.route('/')
def index():
    # Pass the tasks list to our HTML template
    return render_template('index.html', tasks=tasks)

@app.route('/add', methods=['POST'])
def add_task():
    global next_task_id # Declare that we want to modify the global variable

    # Get the 'task' input from the form submission
    task_text = request.form.get('task')
    if task_text: # Ensure the task text is not empty
        tasks.append({'id': next_task_id, 'text': task_text, 'completed': False})
        next_task_id += 1 # Increment for the next task
    # After adding, redirect the user back to the home page
    return redirect(url_for('index'))

@app.route('/complete/<int:task_id>', methods=['POST'])
def complete_task(task_id):
    for task in tasks:
        if task['id'] == task_id:
            task['completed'] = True
            break # Stop once the task is found and updated
    return redirect(url_for('index'))

@app.route('/delete/<int:task_id>', methods=['POST'])
def delete_task(task_id):
    global tasks
    # Recreate the tasks list, excluding the task to be deleted
    tasks = [task for task in tasks if task['id'] != task_id]
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)

Quick Explanations for the Updated app.py:

  • from flask import Flask, render_template, request, redirect, url_for: We’ve added render_template (to render our HTML files), request (to access incoming request data like form submissions), redirect (to send the user to a different URL), and url_for (to dynamically build URLs).
  • tasks = [] and next_task_id: This is our simple in-memory storage for to-do items. Each item will be a dictionary.
  • @app.route('/'): This is our home page. It now calls render_template('index.html', tasks=tasks) to display our index.html file and pass the tasks list to it.
  • @app.route('/add', methods=['POST']):
    • methods=['POST'] means this route will only respond to HTTP POST requests. We use POST when submitting data that changes the server’s state (like adding a new task).
    • request.form.get('task') retrieves the value from the HTML input field named task.
    • redirect(url_for('index')): After processing the task addition, we redirect the user back to the home page. This is a common pattern called “Post/Redirect/Get” (PRG) to prevent duplicate form submissions if the user refreshes the page.
  • @app.route('/complete/<int:task_id>', methods=['POST']):
    • <int:task_id> is a “variable part” in the URL. It tells Flask to capture the number in that part of the URL and pass it as the task_id argument to our complete_task function.
    • We then loop through our tasks list to find the matching task and update its completed status.
  • @app.route('/delete/<int:task_id>', methods=['POST']):
    • Similar to complete_task, it captures the task_id from the URL.
    • tasks = [task for task in tasks if task['id'] != task_id] is a Python “list comprehension” that creates a new list containing all tasks except the one with the matching task_id. This effectively deletes the task.

Running Your To-Do App

  1. Save all files: Make sure you’ve saved app.py and templates/index.html.
  2. Activate virtual environment: If you closed your terminal, remember to activate your virtual environment again.
  3. Run Flask:

    bash
    python app.py

  4. Open in browser: Go to http://127.0.0.1:5000 in your web browser.

You should now see your To-Do List app! Try adding tasks, marking them complete, and deleting them. Watch how the page updates without a full reload each time thanks to the forms and redirects.

Next Steps & Ideas for Improvement

You’ve built a functional To-Do List app with Flask! Here are some ideas for how you can take it further:

  • Persistence (Using a Database): Currently, your tasks disappear when you restart the server. To make them permanent, you’d integrate a database. SQLite is an excellent choice for small projects and easy to get started with using Flask-SQLAlchemy.
  • User Interface (CSS/JavaScript): While we added some basic inline CSS, you could create a separate static folder for external CSS files and JavaScript to make your app look much nicer and more interactive.
  • User Authentication: Add login/logout features so multiple users can have their own private To-Do lists.
  • Form Validation: Ensure users enter valid data (e.g., prevent empty task submissions on the server side).
  • Deployment: Learn how to deploy your Flask app to a live server so others can use it. Services like Heroku, PythonAnywhere, or Render are popular choices for beginners.

Conclusion

Congratulations! You’ve successfully built a simple To-Do List application using Flask. You’ve learned how to set up a Flask project, use virtual environments, define routes, render HTML templates, and handle form submissions. These are fundamental skills that will serve as a strong foundation for building more complex web applications in the future. Keep experimenting, keep coding, and most importantly, have fun with Flask!


Comments

Leave a Reply