Skip to main content

SQLAlchemy building blocks for Belgie

Project description

belgie-alchemy

SQLAlchemy 2.0 utilities for Belgie.

Overview

belgie-alchemy provides the BelgieAdapter, auth model mixins, and database settings for Belgie. For SQLAlchemy building blocks (Base, low-level mixins, types), use brussels:

  • Base: Declarative base with dataclass mapping and sensible defaults
  • Mixins: PrimaryKeyMixin (UUID), TimestampMixin (created/updated/deleted timestamps)
  • Types: DateTimeUTC (timezone-aware datetimes), Json (dialect-specific JSON storage)

The examples below keep model ownership in your app while reducing boilerplate.

Quick Start

from datetime import datetime
from brussels.base import DataclassBase
from brussels.mixins import PrimaryKeyMixin, TimestampMixin
from brussels.types import DateTimeUTC
from sqlalchemy.orm import Mapped, mapped_column

class Article(DataclassBase, PrimaryKeyMixin, TimestampMixin):
    __tablename__ = "articles"

    title: Mapped[str]
    published_at: Mapped[datetime] = mapped_column(DateTimeUTC)

This gives you:

  • UUID primary key with server-side generation
  • Automatic created_at, updated_at, deleted_at timestamps
  • Timezone-aware datetime handling
  • Dataclass-style __init__, __repr__, __eq__

Building Blocks

Base

Declarative base with dataclass mapping enabled:

from brussels.base import DataclassBase
from sqlalchemy.orm import Mapped, mapped_column

class MyModel(DataclassBase):
    __tablename__ = "my_models"

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

# Dataclass-style instantiation
model = MyModel(id=1, name="example")

Features:

  • Consistent naming conventions for constraints
  • Automatic type annotation mapping (datetimeDateTimeUTC)
  • Dataclass mapping for convenient instantiation

Mixins

PrimaryKeyMixin

Adds a UUID primary key with server-side generation:

from brussels.base import DataclassBase
from brussels.mixins import PrimaryKeyMixin

class MyModel(DataclassBase, PrimaryKeyMixin):
    __tablename__ = "my_models"
    # Automatically includes: id: Mapped[UUID]

The id field:

  • Type: UUID
  • Server-generated using gen_random_uuid()
  • Indexed and unique
  • Primary key

TimestampMixin

Adds automatic timestamp tracking:

from brussels.base import DataclassBase
from brussels.mixins import TimestampMixin

class MyModel(DataclassBase, TimestampMixin):
    __tablename__ = "my_models"
    # Automatically includes:
    # - created_at: Mapped[datetime]
    # - updated_at: Mapped[datetime] (auto-updates on changes)
    # - deleted_at: Mapped[datetime | None]

Features:

  • created_at set automatically on insert
  • updated_at auto-updates on row changes
  • deleted_at for soft deletion
  • mark_deleted() method to set deleted_at

Types

DateTimeUTC

Timezone-aware datetime storage:

from datetime import datetime
from brussels.base import DataclassBase
from brussels.types import DateTimeUTC
from sqlalchemy.orm import Mapped, mapped_column

class Event(DataclassBase):
    __tablename__ = "events"

    id: Mapped[int] = mapped_column(primary_key=True)
    happened_at: Mapped[datetime] = mapped_column(DateTimeUTC)

Features:

  • Automatically converts naive datetimes to UTC
  • Preserves timezone-aware datetimes
  • Always returns UTC-aware datetimes from database
  • Works with PostgreSQL, SQLite, MySQL

Json

Dialect-specific JSON storage (JSONB on PostgreSQL):

from brussels.base import DataclassBase
from brussels.types import Json
from sqlalchemy.orm import Mapped, mapped_column

class User(DataclassBase):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    # Store scopes as JSON (works everywhere)
    scopes: Mapped[list[str] | None] = mapped_column(Json, default=None)

Features:

  • PostgreSQL: Uses JSONB
  • SQLite/MySQL: Uses JSON

For PostgreSQL with application-specific enum types, you can override:

from enum import StrEnum
from brussels.base import DataclassBase
from sqlalchemy import ARRAY
from sqlalchemy.dialects.postgresql import ENUM
from sqlalchemy.orm import Mapped, mapped_column

class AppScope(StrEnum):
    READ = "resource:read"
    WRITE = "resource:write"
    ADMIN = "admin"

class User(DataclassBase):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    # Option 2: PostgreSQL native ENUM array (type-safe)
    scopes: Mapped[list[AppScope] | None] = mapped_column(
        ARRAY(ENUM(AppScope, name="app_scope", create_type=True)),
        default=None,
    )

Auth Model Mixins

Use the built-in auth mixins for a minimal model setup:

from brussels.base import DataclassBase
from belgie_alchemy import AccountMixin, OAuthStateMixin, SessionMixin, UserMixin

class User(DataclassBase, UserMixin):
    pass

class Account(DataclassBase, AccountMixin):
    pass

class Session(DataclassBase, SessionMixin):
    pass

class OAuthState(DataclassBase, OAuthStateMixin):
    pass

Defaults include:

  • User email/profile fields and JSON scopes (Json, JSONB on PostgreSQL)
  • Account provider linkage fields and uniqueness constraint
  • Session expiration and metadata fields
  • OAuth state PKCE fields and optional user linkage
  • UUID primary keys and timestamps on all models
  • PostgreSQL CITEXT variants for case-insensitive email, provider, and provider_account_id

For PostgreSQL deployments, ensure the citext extension is installed when using the default mixins.

You can still override any field, relationship, or __tablename__ in your concrete model classes.

See examples/alchemy/auth_models.py for a complete reference implementation.

Design Principles

  1. Building blocks, not frameworks - You own your models completely
  2. Sensible defaults - UTC datetimes, UUIDs, timestamps by default
  3. Dataclass-friendly - Clean instantiation and repr
  4. Dialect-aware - Use the best type for each database
  5. Minimal magic - Clear, explicit behavior

Migration from impl/auth.py

If you previously imported models from belgie_alchemy.impl.auth:

Before:

from belgie_alchemy.impl.auth import User, Account, Session, OAuthState

After:

# Build your own concrete classes from mixins:
from brussels.base import DataclassBase
from belgie_alchemy import AccountMixin, OAuthStateMixin, SessionMixin, UserMixin

class User(DataclassBase, UserMixin): ...
class Account(DataclassBase, AccountMixin): ...
class Session(DataclassBase, SessionMixin): ...
class OAuthState(DataclassBase, OAuthStateMixin): ...

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

belgie_alchemy-0.7.0.tar.gz (9.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

belgie_alchemy-0.7.0-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

Details for the file belgie_alchemy-0.7.0.tar.gz.

File metadata

  • Download URL: belgie_alchemy-0.7.0.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie_alchemy-0.7.0.tar.gz
Algorithm Hash digest
SHA256 75ce7f0ca5f832d0bba95f6e6a04e17aa23fef61cf10052a0aa3c56a9c294554
MD5 fd5f48f4d08266f3db0fc61948bb21ee
BLAKE2b-256 311e94d801fd1664cb63b58b4fb29fda0a323da601f6b20f3b223eb33d95c068

See more details on using hashes here.

File details

Details for the file belgie_alchemy-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: belgie_alchemy-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 15.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie_alchemy-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 64f9c0bc60c1d16c1c66a565eb7195d035b93360ee14e5b7ea2feb59462bb221
MD5 cac15fa670f59d3beeab7c63c47f0bf6
BLAKE2b-256 841612e1444c104d994dd2975b020c3ae715ca07df0d7190cdbb21382e8b2198

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page