Skip to content

Alamo-Tech-Collective/ToDo

Repository files navigation

Django + HTMX To-Do App Workshop

A hands-on workshop project for Alamo Tech Collective (in collaboration with Alamo Python) that builds a to-do app with Django + HTMX — instant, partial-page updates and not a line of hand-written JavaScript.

Project Overview

Every action in this app — add a task, check it off, delete it, flip to the next page — updates just the part of the page that changed, with no full-page reload. The trick is HTMX: a small library you load from a CDN, a handful of hx-* attributes in the templates, and Django views that return HTML fragments instead of whole pages. That's the whole story, and this app is here to show it end to end.

Prerequisites

  • Docker Desktop installed and running
  • Git (for cloning the repository)
  • That's it! No Python, Django, or other dependencies needed locally.

Quick Start

1. Clone the Repository

git clone https://github.com/Alamo-Tech-Collective/django-htmx-todo.git
cd django-htmx-todo

2. Start the Application

docker compose up

Wait for the startup messages. You should see:

Starting development server at http://0.0.0.0:8000/

3. Open Your Browser

Visit: http://localhost:8000

You should see the to-do app running!

4. Try It Out

  • Add some tasks
  • Check/uncheck tasks (just that row updates — no reload)
  • Delete tasks (the row disappears — no reload)
  • Add 6+ tasks to see pagination (paging swaps just the list, and the URL still updates so you can bookmark ?page=2)

Key observation: Nothing flashes or reloads. Open your browser's Network tab and watch — each action is a tiny request that returns a snippet of HTML, and HTMX drops it into the page.

Stopping the Application

Press Ctrl+C in the terminal, or run:

docker compose down

How HTMX Fits In

The whole HTMX integration is three moves — worth tracing once you've got the app running:

  1. Load HTMX. One CDN <script> tag in tasks/templates/tasks/base.html. That same file sets hx-headers on <body> so Django's CSRF token rides along on every HTMX request.
  2. Mark up the controls. In the templates, the add form, the checkboxes, the delete buttons, and the pagination links carry hx-post/hx-get, hx-target, and hx-swap — telling HTMX what to request and where to put the response.
  3. Return fragments. tasks/views.py returns a partial template (a single row, or the list region) instead of a full page. Compare tasks/templates/tasks/task_list.html (the full page) with tasks/templates/tasks/partials/ (the pieces the views swap in).

Trace one action end to end — toggling a checkbox is a good one — and you've seen basically everything this app does.

Project Structure

django-htmx-todo/
├── README.md                   # This file
├── CLAUDE.md                   # Project context and guidelines
├── docker-compose.yml          # Docker orchestration
├── Dockerfile                  # Container definition
├── requirements.txt            # Python dependencies
├── manage.py                   # Django management script
├── todo_project/               # Django project settings
│   ├── settings.py
│   └── urls.py
└── tasks/                      # To-do app
    ├── models.py              # Task model
    ├── views.py               # View logic
    ├── forms.py               # Django forms
    ├── urls.py                # URL routing
    ├── admin.py               # Django admin
    ├── templates/             # HTML templates
    │   └── tasks/
    │       ├── base.html      # loads HTMX, sets the CSRF header
    │       ├── task_list.html # full page: add form + the list partial
    │       └── partials/      # the fragments the views swap in
    │           ├── task_row.html   # one task <li>
    │           └── task_list.html  # the list region + pagination
    └── static/                # CSS
        └── css/
            └── style.css

Features

Core Functionality

  • ✅ Create tasks (the list region re-renders, no full reload)
  • ✅ View tasks (with pagination — 5 per page, swapped via HTMX)
  • ✅ Toggle task completion (just that row updates)
  • ✅ Delete tasks (the row is removed from the DOM)
  • ✅ Django admin interface

How It's Built

  • Django views return HTML fragments, not just full pages
  • hx-post / hx-get + hx-target + hx-swap wire the UI to those views
  • CSRF handled once via hx-headers on <body>
  • No hand-written JavaScript — HTMX is the only script on the page

Workshop Goals

This project is designed to teach:

  1. The HTMX model — request, target, swap; HTML over the wire
  2. The Django side — returning partials and reusing templates via {% include %}
  3. When to use HTMX vs full JavaScript frameworks
  4. Real-world patterns you can use in your projects

Workshop Day — What to Expect

Plan for about 90–120 minutes, hands-on the whole way. Here's the shape of it:

Phase 1 — Run it & read it (~40–50 min). Everyone gets the app running with docker compose up, uses it (notice: nothing reloads), then we walk the code — models, views, forms, and the templates. We'll trace one action, like toggling a task, from the hx-post in the template all the way to the view that returns the row.

Phase 2 — Build a feature (~30–45 min). We cover the HTMX fundamentals (hx-post/hx-get, hx-target, hx-swap) and how a Django view returns a fragment, then add a feature together and you take a crack at one yourself.

Phase 3 — Q&A and wrap-up (~15 min). When to reach for HTMX vs. React/Vue, scaling considerations, and where to go next.

Come with Docker Desktop installed and the repo cloned (see Setup above). The less time we spend on environment issues, the more we spend building.

Troubleshooting

Docker Issues

Problem: Cannot connect to the Docker daemon

# Solution: Make sure Docker Desktop is running
# Then try again: docker compose up

Problem: Port 8000 already in use

# Solution: Stop the other process using port 8000, or change the port in docker-compose.yml
# Find what's using port 8000:
lsof -i :8000
# Kill it or change docker-compose.yml ports to "8001:8000"

Problem: Changes not appearing

# Solution: Rebuild the container
docker compose down
docker compose up --build

Application Issues

Problem: No tasks showing up

# Solution: Check if migrations ran
docker compose exec web python manage.py migrate

Problem: CSS not loading

# Solution: Collect static files
docker compose exec web python manage.py collectstatic --noinput
# Or just refresh the page (volume mounting should auto-update)

Development

Running Management Commands

# Create a superuser for Django admin
docker compose exec web python manage.py createsuperuser

# Access Django admin at: http://localhost:8000/admin

# Create migrations (if you modify models)
docker compose exec web python manage.py makemigrations

# Apply migrations
docker compose exec web python manage.py migrate

# Django shell
docker compose exec web python manage.py shell

# Run the test suite
docker compose exec web python manage.py test

Viewing Logs

# Follow logs in real-time
docker compose logs -f web

# View last 50 lines
docker compose logs --tail=50 web

Technology Stack

  • Backend: Django 5.2 (LTS)
  • Database: SQLite (simple, no external dependencies)
  • Frontend: Django templates + HTMX 2.x (loaded from CDN)
  • Styling: Minimal custom CSS
  • Container: Docker + Docker Compose

Contributing

This is an educational project for Alamo Tech Collective workshops. Contributions are welcome!

Resources

License

MIT License - feel free to use this for your own learning and projects!

Questions?

  • Workshop attendees: Ask during the workshop or in the Slack channel
  • Everyone else: Open an issue on GitHub
  • Visit us: Alamo Tech Collective

Built with ❤️ by Alamo Tech Collective & Alamo Python community

About

Django + HTMX Workshop

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors