Create SQLAlchemy base model with UUID, timestamp, and soft delete mixins for FastAPI
FastAPI Core Models
Overview
This skill covers creating the base SQLAlchemy model and reusable mixins for UUID primary keys, timestamps, and soft delete functionality.
Create core/models.py
Create src/app/core/models.py:
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import DateTime, func
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.orm import DeclarativeBase, Mapped, declared_attr, mapped_column
from sqlalchemy.ext.asyncio import AsyncAttrs
classBase(AsyncAttrs, DeclarativeBase):
"""
Base class for all SQLAlchemy models.
Includes AsyncAttrs for proper async lazy loading support.
All models should inherit from this class.
""" @declared_attr.directivedef__tablename__(cls) -> str:
"""
Generate table name from class name.
Converts CamelCase to snake_case and pluralizes.
Example: UserProfile -> user_profiles
"""
re
name = re.sub(, , cls.__name__).lower()
:
: Mapped[UUID] = mapped_column(
PG_UUID(as_uuid=),
primary_key=,
default=uuid4,
sort_order=-,
)
:
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=),
server_default=func.now(),
nullable=,
sort_order=,
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=),
server_default=func.now(),
onupdate=func.now(),
nullable=,
sort_order=,
)
:
deleted_at: Mapped[datetime | ] = mapped_column(
DateTime(timezone=),
nullable=,
default=,
index=,
sort_order=,
)
() -> :
.deleted_at
import
r"(?<!^)(?=[A-Z])"
"_"
return
f"{name}s"
class
UUIDMixin
"""
Mixin that adds a UUID primary key.
Uses PostgreSQL's native UUID type for optimal storage and indexing.
Generates UUID4 by default.
"""
id
True
True
100
# Ensure id appears first in table
class
TimestampMixin
"""
Mixin that adds created_at and updated_at timestamps.
- created_at: Set once when record is created (server-side default)
- updated_at: Updated automatically on every modification
All timestamps are timezone-aware UTC.
"""
True
False
100
True
False
101
class
SoftDeleteMixin
"""
Mixin that adds soft delete functionality.
- deleted_at: NULL means not deleted, timestamp means deleted
- Records are never physically deleted, only marked
Repositories should filter out soft-deleted records by default.
"""
None
True
True
None
True
# Index for efficient filtering
102
@property
def
is_deleted
self
bool
"""Check if the record is soft deleted."""
return
self
is
not
None
Usage Example
When creating entity models, combine the mixins:
# src/app/items/models.pyfrom sqlalchemy import String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.models import Base, UUIDMixin, TimestampMixin, SoftDeleteMixin
classItem(UUIDMixin, TimestampMixin, SoftDeleteMixin, Base):
"""Item model with UUID, timestamps, and soft delete."""
__tablename__ = "items"# Explicit table name (optional)
name: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
Generated Table Structure
The above model generates:
CREATE TABLE items (
id UUID PRIMARY KEYDEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
created_at TIMESTAMPWITHTIME ZONE DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPWITHTIME ZONE DEFAULT NOW() NOT NULL,
deleted_at TIMESTAMPWITHTIME ZONE
);
CREATE INDEX ix_items_deleted_at ON items (deleted_at);