Basics

Advanced Alchemy provides pre-configured base classes for SQLAlchemy models with common primary key strategies and audit fields.

Base Classes

Advanced Alchemy includes several declarative base classes. Each provides different primary key strategies:

Base Classes and Features

Base Class

Features

BigIntBase

BIGINT primary keys for tables

BigIntAuditBase

BIGINT primary keys for tables, Automatic created_at/updated_at timestamps

IdentityBase

Primary keys using database IDENTITY feature instead of sequences

IdentityAuditBase

Primary keys using database IDENTITY feature, Automatic created_at/updated_at timestamps

UUIDBase

UUID primary keys

UUIDv6Base

UUIDv6 primary keys

UUIDv7Base

UUIDv7 primary keys

UUIDAuditBase

UUID primary keys, Automatic created_at/updated_at timestamps

UUIDv6AuditBase

UUIDv6 primary keys, Automatic created_at/updated_at timestamps

UUIDv7AuditBase

Time-sortable UUIDv7 primary keys, Automatic created_at/updated_at timestamps

NanoIDBase

URL-friendly unique identifiers, Shorter than UUIDs, collision resistant

NanoIDAuditBase

URL-friendly IDs with audit timestamps, Combines Nanoid benefits with audit trails

Basic Mixins

Advanced Alchemy provides mixins to enhance model functionality:

Available Mixins

Mixin

Features

SlugKey

Adds URL-friendly slug field

AuditColumns

Automatic created_at/updated_at timestamps
Tracks record modifications
updated_at refreshes during flush when any mapped column value changes, while preserving explicit timestamp overrides

BigIntPrimaryKey

Adds BigInt primary key with sequence

IdentityPrimaryKey

Adds primary key using database IDENTITY feature

Simple Model Example

Creating a basic model with BigIntAuditBase:

import datetime
from typing import Optional

from advanced_alchemy.base import BigIntAuditBase
from sqlalchemy.orm import Mapped, mapped_column

class Post(BigIntAuditBase):
    """Blog post model with auto-incrementing ID and audit fields.

    Attributes:
        title: The post title
        content: The post content
        published: Publication status
        published_at: Timestamp of publication
        created_at: Timestamp of creation (from BigIntAuditBase)
        updated_at: Timestamp of last update (from BigIntAuditBase)
    """

    title: Mapped[str] = mapped_column(index=True)
    content: Mapped[str]
    published: Mapped[bool] = mapped_column(default=False)
    published_at: Mapped[Optional[datetime.datetime]] = mapped_column(default=None)
import datetime

from advanced_alchemy.base import BigIntAuditBase
from sqlalchemy.orm import Mapped, mapped_column

class Post(BigIntAuditBase):
    """Blog post model with auto-incrementing ID and audit fields.

    Attributes:
        title: The post title
        content: The post content
        published: Publication status
        published_at: Timestamp of publication
        created_at: Timestamp of creation (from BigIntAuditBase)
        updated_at: Timestamp of last update (from BigIntAuditBase)
    """

    title: Mapped[str] = mapped_column(index=True)
    content: Mapped[str]
    published: Mapped[bool] = mapped_column(default=False)
    published_at: Mapped[datetime.datetime | None] = mapped_column(default=None)

This model includes:

  • Auto-incrementing id column (BigInt primary key)

  • created_at timestamp (set on creation)

  • updated_at timestamp (refreshed on modification)

  • Custom columns: title, content, published, published_at

Implementation Patterns

UUID vs BigInt Primary Keys

Different primary key types have distinct characteristics:

BigInt Primary Keys

from advanced_alchemy.base import BigIntAuditBase

class User(BigIntAuditBase):
    username: Mapped[str] = mapped_column(unique=True)

Characteristics:

  • Sequential integers (1, 2, 3, …)

  • Smaller index size compared to UUIDs

  • Database generates values via sequences

  • Predictable ordering

  • Visible in URLs (/users/123)

UUID Primary Keys

from advanced_alchemy.base import UUIDAuditBase

class User(UUIDAuditBase):
    username: Mapped[str] = mapped_column(unique=True)

Characteristics:

  • Random 128-bit identifiers

  • Generated client-side (Python)

  • Suitable for distributed systems

  • Non-sequential

  • Not guessable in URLs (/users/550e8400-e29b-41d4-a716-446655440000)

UUIDv7 Primary Keys

from advanced_alchemy.base import UUIDv7AuditBase

class User(UUIDv7AuditBase):
    username: Mapped[str] = mapped_column(unique=True)

Characteristics:

  • Time-ordered UUIDs

  • Timestamp embedded in first 48 bits

  • Combines benefits of sequential and random IDs

  • Better database index performance than UUIDv4

  • Sortable by creation time

NanoID Primary Keys

from advanced_alchemy.base import NanoIDAuditBase

class User(NanoIDAuditBase):
    username: Mapped[str] = mapped_column(unique=True)

Characteristics:

  • URL-friendly strings (uses A-Za-z0-9_-)

  • Shorter than UUIDs (21 characters vs 36)

  • Collision resistant

  • Generated client-side

  • Requires nanoid extra: pip install advanced-alchemy[nanoid]

Using Mixins

Mixins add functionality to models without inheritance from base classes:

from advanced_alchemy.base import UUIDBase
from advanced_alchemy.mixins import SlugKey, AuditColumns
from sqlalchemy.orm import Mapped, mapped_column

class Article(UUIDBase, SlugKey, AuditColumns):
    """Article with UUID primary key, slug, and audit fields."""

    title: Mapped[str] = mapped_column(unique=True)
    content: Mapped[str]

This model combines:

  • UUIDBase - UUID primary key

  • SlugKey - Automatic slug field

  • AuditColumns - created_at/updated_at timestamps

Technical Constraints

Audit Field Behavior

The AuditColumns mixin provides automatic timestamps:

# ✅ Correct - updated_at refreshes automatically
user = await repository.get_one(User.id == user_id)
user.email = "new@example.com"
await session.commit()
# user.updated_at is automatically updated

# ✅ Correct - explicit override is preserved
user.updated_at = specific_timestamp
await session.commit()
# user.updated_at retains the explicit value

The updated_at field refreshes during flush when any mapped column changes, but preserves explicitly set values.

Primary Key Generation

Primary key generation differs by type:

# BigInt - database generates via sequence
user = User(username="alice")  # No id needed
session.add(user)
await session.flush()
# user.id is populated by database

# UUID - Python generates client-side
user = User(username="bob")  # UUID generated automatically
# user.id is populated before flush

# NanoID - Python generates client-side
user = User(username="charlie")  # NanoID generated automatically
# user.id is populated before flush

Next Steps

Once you have basic models, you can add relationships between them.

See Relationships for foreign keys and many-to-many patterns.