1 Easy Way To Building a Flask To-Do List App: A Mini-Course for Beginners

Abdalla Harem | September 10, 2025 | 6 min read

Welcome to this hands-on mini-course on building a full-stack web application using Flask! If you’re new to web development or looking to level up your Python skills, this guide is for you. We’ll create a simple yet functional to-do list app that allows users to add, update, and delete tasks. We’ll integrate a SQLite database for data persistence and deploy it to Heroku for free hosting.

What You’ll Learn:

  • Setting up a Flask app with routes and templates.
  • Managing data with SQLite and Flask-SQLAlchemy.
  • Creating a simple CRUD (Create, Read, Update, Delete) interface.
  • Deploying to Heroku for free hosting.

Prerequisites: Basic Python knowledge. No prior Flask or web dev experience needed!

Tools You’ll Need:

  • Python 3.12: Core language for Flask (download from python.org).
  • VS Code: Code editor for writing Python and HTML (download from code.visualstudio.com).
  • Terminal: Use Terminal (macOS/Linux) or PowerShell (Windows) for commands.
  • Git: Version control for deployment (install from git-scm.com).
  • Heroku CLI: For deploying to Heroku (install from devcenter.heroku.com).
  • pip: Python package manager (comes with Python).
  • Virtualenv: For isolated Python environments (included with Python).
  • Web Browser: Chrome, Firefox, or Edge to test the app.

Let’s build this step-by-step!

Welcome to this hands-on mini-course on building a full-stack web application using Flask! If you're new to web development or looking to level up your Python skills, this guide is for you. We'll create a simple yet functional to-do list app that allows users to add, update, and delete tasks. We'll integrate a SQLite database for data persistence and deploy it to Heroku for free hosting.

Module 1: Setting Up Your Environment

Get your workspace ready with the right tools and structure.

  1. Create Project Directory:
    • Tool: Terminal (macOS/Linux) or PowerShell (Windows).
    • Run:
mkdir todo_app && cd todo_app
  • Why? Organizes your project files.

2. Set Up Virtual Environment:

  • Tool: Python (python3 or python command).
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
  • Why? Isolates dependencies to avoid conflicts.

3. Install Dependencies:

  • Tool: pip (Python package manager).
  • Install Flask (web framework), Flask-SQLAlchemy (database ORM), and Gunicorn (production server):
pip install flask flask-sqlalchemy gunicorn
pip freeze > requirements.txt
  • Why? requirements.txt ensures Heroku knows what to install.

4. Create Project Structure:

  • Tool: Terminal or VS Code (File Explorer).
  • Run:
mkdir templates static
touch app.py models.py requirements.txt Procfile runtime.txt
    • Structure:
      • app.py: Main app logic.
      • models.py: Database models.
      • templates/: HTML files.
      • static/: CSS/JS files.
      • Procfile & runtime.txt: Heroku configs.
    • Why? Follows Flask’s conventions for scalability.

Artifact: Requirements File

Flask==3.0.3 Flask-SQLAlchemy==3.1.1 gunicorn==22.0.0 Werkzeug==3.0.3

Pro Tip: Use VS Code’s Python extension for linting and debugging.

Module 2: Defining the Database Model

We’ll use SQLite (a lightweight, file-based database) with Flask-SQLAlchemy to manage tasks.

  • Tools:
    • VS Code: To write models.py.
    • Flask-SQLAlchemy: ORM for database interactions.
    • SQLite: Built into Python (no separate install needed).

Create models.py:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    description = db.Column(db.String(500))
    completed = db.Column(db.Boolean, default=False)

    def __repr__(self):
        return f'<Task {self.title}>'

Breakdown:

  • db = SQLAlchemy(): Initializes ORM.
  • Task class: Defines table structure.
    • id: Unique identifier.
    • title: Required task name (max 200 chars).
    • description: Optional details.
    • completed: Tracks completion status.
  • __repr__: For debugging.

Key Learning: ORMs simplify database queries into Python objects, reducing manual SQL.


Module 3: Building the Flask App

This is the core—routes, logic, and database integration.

  • Tools:
    • VS Code: For coding app.py.
    • Flask: Web framework for routing and rendering.
    • SQLite: Stores data in tasks.db.

Create app.py:

from flask import Flask, render_template, request, redirect, url_for, flash
from models import db, Task
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-change-this-in-production'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db.init_app(app)

# Create database tables
with app.app_context():
    db.create_all()

@app.route('/')
def index():
    tasks = Task.query.all()
    return render_template('index.html', tasks=tasks)

@app.route('/add', methods=['POST'])
def add_task():
    title = request.form['title']
    description = request.form['description']
    if title:
        new_task = Task(title=title, description=description)
        db.session.add(new_task)
        db.session.commit()
        flash('Task added successfully!')
    else:
        flash('Title is required!')
    return redirect(url_for('index'))

@app.route('/update/<int:task_id>', methods=['POST'])
def update_task(task_id):
    task = Task.query.get_or_404(task_id)
    task.completed = not task.completed
    db.session.commit()
    flash('Task updated!')
    return redirect(url_for('index'))

@app.route('/delete/<int:task_id>', methods=['POST'])
def delete_task(task_id):
    task = Task.query.get_or_404(task_id)
    db.session.delete(task)
    db.session.commit()
    flash('Task deleted!')
    return redirect(url_for('index'))

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port, debug=False)

Breakdown:

  • Config: Secret key for security, SQLite URI, and disable tracking.
  • Routes:
    • /: Lists all tasks.
    • /add: Creates new tasks.
    • /update/<task_id>: Toggles completion.
    • /delete/<task_id>: Deletes tasks.
  • Flash: User feedback messages.
  • Production-Ready: Uses os.environ.get(‘PORT’) for Heroku.

Pro Tip: Use url_for for dynamic URLs and flash for user feedback.


Module 4: Creating the Frontend

We’ll use HTML with Jinja2 templating and Bootstrap for styling.

  • Tools:
    • VS Code: For writing HTML.
    • Bootstrap 5.1.3: Via CDN for styling (no install needed).
    • Jinja2: Flask’s templating engine.

Create templates/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>To-Do List</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container mt-5">
    <h1 class="mb-4">My To-Do List</h1>
    
    <!-- Add Task Form -->
    <form method="POST" action="{{ url_for('add_task') }}" class="mb-4">
        <div class="row">
            <div class="col-md-6">
                <input type="text" class="form-control" name="title" placeholder="Task Title" required>
            </div>
            <div class="col-md-4">
                <input type="text" class="form-control" name="description" placeholder="Description (optional)">
            </div>
            <div class="col-md-2">
                <button type="submit" class="btn btn-primary w-100">Add Task</button>
            </div>
        </div>
    </form>

    <!-- Flash Messages -->
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            {% for message in messages %}
                <div class="alert alert-info">{{ message }}</div>
            {% endfor %}
        {% endif %}
    {% endwith %}

    <!-- Tasks List -->
    <ul class="list-group">
        {% for task in tasks %}
            <li class="list-group-item d-flex justify-content-between align-items-center">
                <div>
                    <h5 class="{% if task.completed %}text-decoration-line-through text-muted{% endif %}">{{ task.title }}</h5>
                    {% if task.description %}
                        <p class="mb-0 text-muted">{{ task.description }}</p>
                    {% endif %}
                </div>
                <div>
                    <form method="POST" action="{{ url_for('update_task', task_id=task.id) }}" style="display: inline;">
                        <button type="submit" class="btn btn-sm btn-success">Toggle Complete</button>
                    </form>
                    <form method="POST" action="{{ url_for('delete_task', task_id=task.id) }}" style="display: inline;">
                        <button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure?')">Delete</button>
                    </form>
                </div>
            </li>
        {% endfor %}
    </ul>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Breakdown:

  • Bootstrap: Provides responsive design via CDN.
  • Form: Submits to /add route.
  • Jinja2: Loops through tasks, conditionally styles completed ones.
  • Flash Messages: Displays success/error alerts.

Key Learning: Templates separate logic from UI. Bootstrap saves time on styling.


Module 5: Testing Locally

Test your app to ensure it works before deployment.

  • Tools:
    • Flask CLI: Run flask run.
    • Web Browser: To view http://127.0.0.1:5000/.
    • SQLite: Creates tasks.db automatically.
  1. Set environment variable (optional for dev):
export FLASK_APP=app.py  # Windows: set FLASK_APP=app.py

2. Run: textflask run

Open the browser to http://127.0.0.1:5000/.

Test adding, updating, and deleting tasks.

Pro Tip: If errors occur, check the terminal output. Flask’s debug mode (debug=True locally) shows detailed logs.


Module 6: Preparing for Heroku

Heroku requires a few extra files for deployment.

  • Tools:
    • VS Code: For creating Procfile and runtime.txt.
    • Git: For version control and pushing to Heroku.
  1. Create Procfile:

Procfile

web: gunicorn app:app
  1. Create runtime.txt:

python-3.12.3

  1. Verify requirements.txt includes gunicorn.

Note: SQLite works for testing but isn’t ideal for Heroku (data resets on dyno restarts). We’ll add PostgreSQL in the next module.


Module 7: Deploying to Heroku

Host your app online for free!

  • Tools:
    • Heroku CLI: For deployment commands.
    • Git: To push code.
    • PostgreSQL: Optional for persistent data (Heroku add-on).
  1. Install Heroku CLI: Follow Heroku’s guide.
  2. Deploy Steps:
    • Login: heroku login.
    • Create app: heroku create your-app-name (unique name).
    • Initialize Git (if not done):
git init
git add .
git commit -m "Initial commit"
  • Push to Heroku: git push heroku main.
  • Scale dyno: heroku ps:scale web=1.
  • Open app: heroku open.

3. Switch to PostgreSQL (recommended):

  • Add free Postgres:
heroku addons:create heroku-postgresql:mini
  • Update app.py in VS Code:
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL').replace("postgres://", "postgresql://")
  • Commit and push again:

git add app.py
git commit -m “Switch to PostgreSQL”
git push heroku main

Troubleshooting:

  • View logs: heroku logs –tail.
  • If data resets: Ensure PostgreSQL is configured (SQLite is ephemeral on Heroku).

Key Learning: Heroku simplifies hosting but requires specific configs like Procfile and environment variables.

Wrapping Up: What You’ve Built

You’ve created a Flask to-do list app with:

  • A SQLite database (local) or PostgreSQL (Heroku).
  • CRUD functionality: Add, toggle, and delete tasks.
  • A Bootstrap-styled frontend.
  • A live deployment on Heroku.

Next Steps:

  • Add user login with Flask-Login.
  • Include task categories or due dates.
  • Enhance styling with custom CSS in static/style.css.
  • Explore Heroku add-ons like Redis for caching.

Best Practices:

  • Change the secret key for production.
  • Use .env files for sensitive configs (with python-dotenv).
  • Commit code to GitHub for backup.
  • Test edge cases (e.g., empty forms).

Tools Recap:

  • Development: Python, VS Code, pip, virtualenv, Flask, Flask-SQLAlchemy, SQLite.
  • Frontend: Bootstrap (CDN), Jinja2.
  • Deployment: Git, Heroku CLI, Gunicorn, PostgreSQL.

If you hit issues, check Heroku logs or Flask docs. Share your app link in the comments! Happy coding! 🚀

Keywords: Flask, Python, web development, SQLite, Flask-SQLAlchemy, Heroku, PostgreSQL, Jinja2, Bootstrap, to-do list app, CRUD, web app tutorial, backend development, full-stack Python, deployment, Gunicorn, cloud hosting, responsive design, beginner coding, Python project

Hashtags: #Flask #Python #WebDev #LearnToCode #PythonProgramming #FlaskTutorial #WebApp #Heroku #SQLAlchemy #SQLite #PostgreSQL #Jinja2 #Bootstrap #ToDoApp #CRUD #Coding #BackendDev #FullStack #PythonProjects #TechTutorial

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top