Prerequisites
- Continue from part 1
- Start fresh
Part 1 scaffolded with no database, so the app has no
models/ package, no
migrations/ directory and no DATABASE_URL. Add them:This is the one case
feather db init exists for. In an app scaffolded with a
database it fails, because migrations/ is already there.Step 1: The models
models/column.py
models/card.py
models/__init__.py
Step 2: The migration
Step 3: The services
services/column_service.py
services/card_service.py
services/__init__.py
Service base class hands you self.db and self.save(). Raising NotFoundError
from get_by_id means a missing ID becomes a 404 without a single try block in your
routes.
Step 4: The board route
routes/pages/home.py
- A POST returns the rendered partial for the thing it just created.
- A DELETE returns an empty string, because the element is going away.
- Routes live under
/htmx/so it’s obvious at a glance which ones return fragments.
Step 5: The partials
templates/partials/column.html
templates/partials/card.html
The HTMX attributes
Step 6: The board template
templates/pages/board.html
static/js/board.js
Ordinary JavaScript files like
board.js are not Vite entry points. Only vendor,
styles and islands/* are built by Vite, which is why the template loads this one from
the dev server in debug and through url_for in production.window.prompt, and the
JavaScript lives in its own file. Inline scripts are a feather check error.
Step 7: The new CSS
static/css/app.css
Step 8: Test it
- Click Add Column and give it a name.
- Type in the input at the bottom of a column and press Enter.
- Hover a card and click the X. Confirm the dialog.
- Click the X on a column header. Confirm that too.
- Refresh the page. Everything is still there.
Prompt Claude
Checkpoint
- Columns and cards are stored in SQLite
- Creating and deleting either one happens without a page reload
- Deletes ask for confirmation first
- Everything survives a refresh
feather checkpasses
Files you created or changed
What you learned
- Models built from
UUIDMixinandTimestampMixin - Generating and applying migrations
- Services as the home for business logic
@injectfor getting a service into a handler- HTMX attributes for create and delete
- Partial templates as HTMX responses
window.showPrompt()instead of the native dialog
Next: drag and drop
Add ordering to the models and a JavaScript island to move cards between columns.