Claude Code Prompts for SQLAlchemy 2.0

Copy-paste Claude Code prompts for SQLAlchemy 2.0 async — declarative models with Mapped types, Alembic migrations, and selectinload N+1 fixes.

💥 50p impulse-buy: Power Prompts PDF (first 10 buyers) 30 battle-tested Claude Code prompts · 8-page PDF · paste into CLAUDE.md and never re-type a prompt again · 50p impulse-buy, no commitment

SQLAlchemy 2.0 dropped the implicit Query API in favour of select(). Claude handles the new syntax well but tends to drift toward joinedload — fine for single records, disastrous for collections. Lock the loading-strategy rule and the queries Claude produces stay fast.

Prompt 1 — Declarative model with constraints

Add a model 'Invoice' in app/models/invoice.py.

Fields:
- id: Mapped[UUID], primary_key=True, default=uuid4
- account_id: Mapped[UUID], ForeignKey('account.id', ondelete='CASCADE'), indexed
- amount_cents: Mapped[int]
- currency: Mapped[str], String(3)
- status: Mapped[InvoiceStatus] enum, default DRAFT, indexed
- issued_at: Mapped[Optional[datetime]]
- created_at, updated_at: from TimeStampedMixin (already exists — read it first)

Relationships:
- account: Mapped['Account'] = relationship(back_populates='invoices', lazy='raise')
- lines: Mapped[list['InvoiceLine']] = relationship(
    back_populates='invoice', cascade='all, delete-orphan', lazy='raise'
  )

Constraints:
- lazy='raise' on all relationships in this project — never lazy='select'.
  Forces explicit loading at the query site.
- Composite index on (account_id, status).
- Add 'invoices' back_populates on Account.
- Generate an Alembic revision file via alembic revision --autogenerate
  --message 'add invoice'. Output the file content. Do NOT run upgrade.

Prompt 2 — Async query with explicit loading

Add async def get_account_dashboard(session, account_id) at
app/services/dashboard.py that returns:
- The Account (with id, name, plan)
- Its last 10 invoices (newest first) — id, amount_cents, status, issued_at
- For each invoice: the sum of its line totals (denormalized into the response)

Constraints:
- One round trip if possible. If two, justify.
- Use selectinload(Account.invoices) and then within that, selectinload(
  Invoice.lines) — collections are selectinload, not joinedload.
- Use func.sum on InvoiceLine.total_cents in a subquery aliased onto Invoice
  so the result is one row per invoice with the sum included. (Pattern:
  with_only_columns + group_by, then join into the main select.)
- Return a Pydantic v2 model AccountDashboard from app/schemas/dashboard.py
  — define it if missing.
- Add a test in tests/services/test_dashboard.py: fixture an account with 12
  invoices, assert only 10 are returned, ordered correctly, with the sums.

Prompt 3 — Reversible Alembic migration

Rename the column 'amt' on table 'invoice' to 'amount_cents'. Production has
~8M rows.

Produce three Alembic migrations:
1. Add column amount_cents (nullable) + Python op.execute backfill in batches of
   5000 + add NOT NULL. Reversible (downgrade drops the new column).
2. Update the SQLAlchemy model to map amount_cents (separate migration to make
   the deploy step a no-op DB change, but a Python change). Actually — model
   changes aren't migrations. Put step 2 as a code commit, document it in the
   header comment of migration 3.
3. Drop column 'amt'. Downgrade re-adds the column — but downgrade does NOT
   restore data, so warn in the header that this migration is destructive.

Each migration has a downgrade() function. Header docstring explains the deploy
sequence.

CLAUDE.md for SQLAlchemy

## SQLAlchemy 2.0 policy
- Async only. AsyncSession via app/db/session.py.
- All models inherit from Base (declarative) + TimeStampedMixin.
- All relationships lazy='raise'. Eager load at query site with
  selectinload (collections) / joinedload (single).
- Alembic migrations always reversible. Destructive migrations flagged
  in the docstring.
- Never query in models. Repos/services own queries.

Related: FastAPI + Claude.

Frequently asked questions

Does Claude know SQLAlchemy 2.0 syntax?
Yes — it uses Mapped[...] and the new Session.execute(select(...)).scalars() pattern. If your codebase still uses 1.4 legacy syntax (query()), say so or you'll get mixed paradigms.
selectinload vs joinedload?
Default: selectinload for collections (one-to-many), joinedload for one-to-one or many-to-one. Claude follows this rule if stated; without it, it sometimes uses joinedload everywhere and hits the cartesian explosion.
How do I keep Alembic migrations idempotent?
Tell Claude `Every migration must have a reversible downgrade(). Migrations that drop data require explicit confirmation in the migration header.` Then Claude won't write one-way migrations.

Free tools

Cost Calculator → API Cookbook → Diff Summarizer → Skills Browser →

More examples

Claude API Python QuickstartClaude API Node.js / TypeScript QuickstartClaude API Streaming in PythonClaude API Streaming in Node.js / TypeScriptClaude API Tool Use in PythonClaude API Tool Use in Node.js / TypeScript