DEV Community

Sameer Qaiser
Sameer Qaiser

Posted on

Building a To-Do List App in Python (With File Saving)

πŸ“Œ Quick Info

  • Topic: A beginner project that saves data to a file
  • Target Audience: Beginners who know the basics and want a real project
  • Goal: Build a working to-do list that remembers tasks after you close it

1. Introduction

"My first two projects didn't save anything. Close the program, everything gone. Then I learned how to save data to a file β€” and suddenly my programs could remember."


2. What We're Building

A simple CLI to-do list where you can:

  • Add a task
  • List all tasks
  • Mark a task as done
  • Delete a task
  • Save everything to a file

When you reopen the program, your tasks are still there.


3. Why This Project Matters

Most beginner projects don't save anything. That's fine for practice, but it's not how real tools work.

Real apps remember. Your notes app remembers. Your browser history remembers. Your IDE remembers which files you had open.

Learning to save data to a file is the step from "toy" to "useful."


4. The Code

import json
import os

FILE = "todos.json"

def load_tasks():
    if not os.path.exists(FILE):
        return []
    with open(FILE, "r") as f:
        return json.load(f)

def save_tasks(tasks):
    with open(FILE, "w") as f:
        json.dump(tasks, f, indent=2)

def show_tasks(tasks):
    if not tasks:
        print("No tasks yet.")
        return
    for i, task in enumerate(tasks, 1):
        status = "βœ“" if task["done"] else " "
        print(f"{i}. [{status}] {task['title']}")

def add_task(tasks, title):
    tasks.append({"title": title, "done": False})
    save_tasks(tasks)
    print(f"Added: {title}")

def complete_task(tasks, index):
    if 0 <= index < len(tasks):
        tasks[index]["done"] = True
        save_tasks(tasks)
        print(f"Completed: {tasks[index]['title']}")

def delete_task(tasks, index):
    if 0 <= index < len(tasks):
        removed = tasks.pop(index)
        save_tasks(tasks)
        print(f"Deleted: {removed['title']}")

def main():
    tasks = load_tasks()
    while True:
        print("\n1. List  2. Add  3. Complete  4. Delete  5. Quit")
        choice = input("Choose: ").strip()

        if choice == "1":
            show_tasks(tasks)
        elif choice == "2":
            title = input("Task: ").strip()
            if title:
                add_task(tasks, title)
        elif choice == "3":
            show_tasks(tasks)
            try:
                idx = int(input("Complete #: ")) - 1
                complete_task(tasks, idx)
            except ValueError:
                print("Enter a number.")
        elif choice == "4":
            show_tasks(tasks)
            try:
                idx = int(input("Delete #: ")) - 1
                delete_task(tasks, idx)
            except ValueError:
                print("Enter a number.")
        elif choice == "5":
            print("Bye!")
            break
        else:
            print("Invalid choice.")

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

5. How It Works

Storage: Tasks are stored as a list of dictionaries:

[
  {"title": "Buy groceries", "done": false},
  {"title": "Write article", "done": true}
]
Enter fullscreen mode Exit fullscreen mode

Load & save : load_tasks() reads the file if it exists. save_tasks() writes the whole list back every time somethiing changes.

The Loop: The main loop shows a menu, takes input, and calls the right function. It runs until you pick β€œQuit.”


6. Run It

python todo.py
Enter fullscreen mode Exit fullscreen mode

Example session:

1. List  2. Add  3. Complete  4. Delete  5. Quit
Choose: 2
Task: Buy groceries
Added: Buy groceries

1. List  2. Add  3. Complete  4. Delete  5. Quit
Choose: 1
1. [ ] Buy groceries
Enter fullscreen mode Exit fullscreen mode

Close the program. Reopen it. Run 1 again. Your task is still there.


7. What I learned

  • json.dump() and json.load() are how you save and load structured data
  • os.path.exists() checks if a file exists before reading it
  • enumerate() gives you both the index and the value in a loop
  • try/except keeps the program from crashing on bad input
  • Saving on every change means the file is always up to date

8. What To Add Next

  • Due dates β€” add a due field to each task
  • Priorities β€” sort tasks by importance
  • Categories β€” group tasks by project
  • A GUI β€” rebuild it with Tkinter
  • Search β€” find tasks by keyword
  • Undo β€” keep a history of changes

9. Conclusion

"This was the first project I built that felt real. Not because the code was fancy β€” but because it remembered things. That's the difference between a tutorial and a tool.”

Top comments (0)