home / skills / 0xdarkmatter / claude-mods / python-database-patterns

python-database-patterns skill

/skills/python-database-patterns

This skill helps you implement robust Python database patterns with SQLAlchemy 2.0, AsyncIO support, and repository patterns for reliable data access.

npx playbooks add skill 0xdarkmatter/claude-mods --skill python-database-patterns

Review the files below or copy the command above to add this skill to your agents.

Files (7)
SKILL.md
4.7 KB
---
name: python-database-patterns
description: "SQLAlchemy and database patterns for Python. Triggers on: sqlalchemy, database, orm, migration, alembic, async database, connection pool, repository pattern, unit of work."
compatibility: "SQLAlchemy 2.0+, Python 3.10+. Async requires asyncpg (PostgreSQL) or aiosqlite."
allowed-tools: "Read Write Bash"
depends-on: [python-typing-patterns, python-async-patterns]
related-skills: [python-fastapi-patterns]
---

# Python Database Patterns

SQLAlchemy 2.0 and database best practices.

## SQLAlchemy 2.0 Basics

```python
from sqlalchemy import create_engine, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(100))
    email: Mapped[str] = mapped_column(String(255), unique=True)
    is_active: Mapped[bool] = mapped_column(default=True)

# Create engine and tables
engine = create_engine("postgresql://user:pass@localhost/db")
Base.metadata.create_all(engine)

# Query with 2.0 style
with Session(engine) as session:
    stmt = select(User).where(User.is_active == True)
    users = session.execute(stmt).scalars().all()
```

## Async SQLAlchemy

```python
from sqlalchemy.ext.asyncio import (
    AsyncSession,
    async_sessionmaker,
    create_async_engine,
)
from sqlalchemy import select

# Async engine
engine = create_async_engine(
    "postgresql+asyncpg://user:pass@localhost/db",
    echo=False,
    pool_size=5,
    max_overflow=10,
)

# Session factory
async_session = async_sessionmaker(engine, expire_on_commit=False)

# Usage
async with async_session() as session:
    result = await session.execute(select(User).where(User.id == 1))
    user = result.scalar_one_or_none()
```

## Model Relationships

```python
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, Mapped, mapped_column

class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]

    # One-to-many
    posts: Mapped[list["Post"]] = relationship(back_populates="author")

class Post(Base):
    __tablename__ = "posts"

    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str]
    author_id: Mapped[int] = mapped_column(ForeignKey("users.id"))

    # Many-to-one
    author: Mapped["User"] = relationship(back_populates="posts")
```

## Common Query Patterns

```python
from sqlalchemy import select, and_, or_, func

# Basic select
stmt = select(User).where(User.is_active == True)

# Multiple conditions
stmt = select(User).where(
    and_(
        User.is_active == True,
        User.age >= 18
    )
)

# OR conditions
stmt = select(User).where(
    or_(User.role == "admin", User.role == "moderator")
)

# Ordering and limiting
stmt = select(User).order_by(User.created_at.desc()).limit(10)

# Aggregates
stmt = select(func.count(User.id)).where(User.is_active == True)

# Joins
stmt = select(User, Post).join(Post, User.id == Post.author_id)

# Eager loading
from sqlalchemy.orm import selectinload
stmt = select(User).options(selectinload(User.posts))
```

## FastAPI Integration

```python
from fastapi import Depends, FastAPI
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Annotated

async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async with async_session() as session:
        yield session

DB = Annotated[AsyncSession, Depends(get_db)]

@app.get("/users/{user_id}")
async def get_user(user_id: int, db: DB):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404)
    return user
```

## Quick Reference

| Operation | SQLAlchemy 2.0 Style |
|-----------|---------------------|
| Select all | `select(User)` |
| Filter | `.where(User.id == 1)` |
| First | `.scalar_one_or_none()` |
| All | `.scalars().all()` |
| Count | `select(func.count(User.id))` |
| Join | `.join(Post)` |
| Eager load | `.options(selectinload(User.posts))` |

## Additional Resources

- `./references/sqlalchemy-async.md` - Async patterns, session management
- `./references/connection-pooling.md` - Pool configuration, health checks
- `./references/transactions.md` - Transaction patterns, isolation levels
- `./references/migrations.md` - Alembic setup, migration strategies

## Assets

- `./assets/alembic.ini.template` - Alembic configuration template

---

## See Also

**Prerequisites:**
- `python-typing-patterns` - Mapped types and annotations
- `python-async-patterns` - Async database sessions

**Related Skills:**
- `python-fastapi-patterns` - Dependency injection for DB sessions
- `python-pytest-patterns` - Database fixtures and testing

Overview

This skill provides practical SQLAlchemy 2.0 patterns and database best practices for Python projects, covering sync and async engines, ORM models, queries, relationships, and FastAPI integration. It bundles recommended patterns for connection pooling, migrations (Alembic), and transaction handling to help teams build reliable data access layers. The content focuses on concrete examples and quick references that you can apply directly in applications.

How this skill works

It inspects common database tasks and provides idiomatic SQLAlchemy 2.0 code for model declarations, queries, relationships, and session usage both synchronous and asynchronous. It also documents integration points (FastAPI DI), connection pool configuration, and migration strategies with Alembic. The skill groups patterns into snippets, quick-reference operations, and linked deeper references for sessions, pooling, transactions, and migrations.

When to use it

  • When setting up new ORM models and relationships with SQLAlchemy 2.0
  • When migrating synchronous DB code to async SQLAlchemy with asyncpg
  • When integrating DB sessions into FastAPI routes using dependency injection
  • When configuring connection pools, health checks, or tuning performance
  • When planning Alembic migrations or adopting repository/unit-of-work patterns

Best practices

  • Use DeclarativeBase and mapped_column annotations for clear, typed models
  • Prefer async_sessionmaker and create_async_engine for async apps; set expire_on_commit=False
  • Keep sessions short-lived per request (dependency injection in FastAPI) and avoid long-lived sessions
  • Use explicit transactions and unit-of-work boundaries for multi-step changes
  • Eager-load relationships with selectinload when fetching collections to avoid N+1 queries

Example use cases

  • Building an async REST API with FastAPI and async SQLAlchemy sessions per request
  • Writing repository classes that encapsulate queries and unit-of-work transaction scope
  • Tuning pool_size and max_overflow on create_async_engine for predictable concurrency
  • Creating Alembic migration templates and scripted workflows for schema evolution
  • Implementing common query filters, aggregates, joins, and pagination using 2.0 style select

FAQ

Should I use sync or async SQLAlchemy?

Choose async for async frameworks (FastAPI async endpoints) and when using async drivers like asyncpg; use sync for simple scripts or when the app is synchronous.

How do I avoid N+1 queries?

Use eager loading options such as selectinload or joinedload in your select statements to fetch related collections efficiently.