# Example 1: Todo App (single entity)

## models.py

```python
"""Tietokantamallit — SQLAlchemy 2.0, Mapped-tyypitys, SQLite."""
from datetime import date
from sqlalchemy import String, Text, Date, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker

DATABASE_URL = "sqlite:///./app.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

class Base(DeclarativeBase):
    pass

class Todo(Base):
    __tablename__ = "todos"
    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    title: Mapped[str] = mapped_column(String(255))
    description: Mapped[str | None] = mapped_column(Text, default=None)
    due_date: Mapped[date | None] = mapped_column(Date, default=None)
    priority: Mapped[int] = mapped_column(default=1)
    status: Mapped[str] = mapped_column(String(20), default="pending")

Base.metadata.create_all(bind=engine)
```

## schemas.py

```python
from datetime import date
from pydantic import BaseModel, ConfigDict

class TodoCreate(BaseModel):
    title: str
    description: str | None = None
    due_date: date | None = None
    priority: int = 1
    status: str = "pending"

class TodoResponse(TodoCreate):
    id: int
    model_config = ConfigDict(from_attributes=True)
```

## test_main.py — exactly 6 tests per entity

```python
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from main import app, get_db
from models import Base

test_engine = create_engine("sqlite:///./test.db", connect_args={"check_same_thread": False})
TestSession = sessionmaker(autocommit=False, autoflush=False, bind=test_engine)
Base.metadata.create_all(bind=test_engine)

def override_get_db():
    db = TestSession()
    try: yield db
    finally: db.close()

app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)

def test_create_todo():
    response = client.post("/todos/", json={"title": "Osta maitoa", "priority": 2})
    assert response.status_code == 201
    assert "id" in response.json()

def test_list_todos():
    client.post("/todos/", json={"title": "Listattava"})
    response = client.get("/todos/")
    assert response.status_code == 200
    assert len(response.json()) >= 1

def test_get_todo_by_id():
    created = client.post("/todos/", json={"title": "Haettava"}).json()
    response = client.get(f"/todos/{created['id']}")
    assert response.status_code == 200

def test_get_todo_not_found():
    response = client.get("/todos/99999")
    assert response.status_code == 404

def test_update_todo():
    created = client.post("/todos/", json={"title": "Vanha"}).json()
    response = client.put(f"/todos/{created['id']}", json={"title": "Uusi"})
    assert response.status_code == 200

def test_delete_todo():
    created = client.post("/todos/", json={"title": "Poistettava"}).json()
    response = client.delete(f"/todos/{created['id']}")
    assert response.status_code == 204
```

# Example 2: Blog (two entities with ForeignKey)

NOTE: ForeignKey is imported from sqlalchemy, NOT from sqlalchemy.orm!

## models.py

```python
from datetime import datetime
from sqlalchemy import String, Text, DateTime, ForeignKey, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, sessionmaker

DATABASE_URL = "sqlite:///./app.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

class Base(DeclarativeBase):
    pass

class Author(Base):
    __tablename__ = "authors"
    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String(255))
    email: Mapped[str] = mapped_column(String(255), unique=True)
    bio: Mapped[str | None] = mapped_column(Text, default=None)
    posts: Mapped[list["Post"]] = relationship(back_populates="author")

class Post(Base):
    __tablename__ = "posts"
    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    title: Mapped[str] = mapped_column(String(255))
    content: Mapped[str] = mapped_column(Text)
    author_id: Mapped[int] = mapped_column(ForeignKey("authors.id"))
    published_at: Mapped[datetime | None] = mapped_column(DateTime, default=None)
    status: Mapped[str] = mapped_column(String(20), default="draft")
    author: Mapped["Author"] = relationship(back_populates="posts")

Base.metadata.create_all(bind=engine)
```

## schemas.py

```python
from datetime import datetime
from pydantic import BaseModel, ConfigDict

class AuthorCreate(BaseModel):
    name: str
    email: str
    bio: str | None = None

class AuthorResponse(AuthorCreate):
    id: int
    model_config = ConfigDict(from_attributes=True)

class PostCreate(BaseModel):
    title: str
    content: str
    author_id: int
    published_at: datetime | None = None
    status: str = "draft"

class PostResponse(PostCreate):
    id: int
    model_config = ConfigDict(from_attributes=True)
```

## test_main.py — 6 tests per entity, create parent FIRST for child tests

```python
client = TestClient(app)  # same setup as above

def _create_author(name="Kirjailija", email=None):
    if email is None:
        email = f"{name.lower().replace(' ', '.')}@example.com"
    return client.post("/authors/", json={"name": name, "email": email}).json()

def test_create_author():
    response = client.post("/authors/", json={"name": "Aleksis Kivi", "email": "aleksis@example.com"})
    assert response.status_code == 201

def test_list_authors():
    _create_author("Minna Canth", "minna@example.com")
    response = client.get("/authors/")
    assert response.status_code == 200
    assert len(response.json()) >= 1

# ... (same pattern: get_by_id, not_found, update, delete)

def test_create_post():
    author = _create_author("Tove Jansson", "tove@example.com")
    response = client.post("/posts/", json={"title": "Artikkeli", "content": "Sisältö", "author_id": author["id"]})
    assert response.status_code == 201

def test_update_post():
    author = _create_author("Joel Lehtonen", "joel@example.com")
    created = client.post("/posts/", json={"title": "Vanha", "content": "Teksti", "author_id": author["id"]}).json()
    response = client.put(f"/posts/{created['id']}", json={"title": "Uusi", "content": "Muokattu", "author_id": author["id"]})
    assert response.status_code == 200

def test_delete_post():
    author = _create_author("Aino Kallas", "aino@example.com")
    created = client.post("/posts/", json={"title": "Poistettava", "content": "Poistetaan", "author_id": author["id"]}).json()
    response = client.delete(f"/posts/{created['id']}")
    assert response.status_code == 204
```
