Skip to main content

SQLAlchemy building blocks for Belgie

Project description

belgie-alchemy

SQLAlchemy 2.0 utilities for Belgie.

Overview

belgie-alchemy provides the AlchemyAdapter and database settings for Belgie. For SQLAlchemy building blocks (Base, 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 use brussels directly so you can own your models.

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,
    )

Complete Example: Auth Models

See examples/alchemy/auth_models.py for a complete reference implementation of authentication models:

  • User - with email, verification, and scopes
  • Account - OAuth provider linkage
  • Session - user session management
  • OAuthState - OAuth flow state

These are templates - copy them to your project and customize as needed.

Example structure:

from datetime import datetime
from uuid import UUID
from brussels.base import DataclassBase
from brussels.mixins import PrimaryKeyMixin, TimestampMixin
from brussels.types import DateTimeUTC, Json
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship

class User(DataclassBase, PrimaryKeyMixin, TimestampMixin):
    __tablename__ = "users"

    email: Mapped[str] = mapped_column(unique=True, index=True)
    email_verified: Mapped[bool] = mapped_column(default=False)
    scopes: Mapped[list[str] | None] = mapped_column(Json, default=None)

    accounts: Mapped[list["Account"]] = relationship(
        back_populates="user",
        cascade="all, delete-orphan",
        init=False,
    )

class Account(DataclassBase, PrimaryKeyMixin, TimestampMixin):
    __tablename__ = "accounts"

    user_id: Mapped[UUID] = mapped_column(
        ForeignKey("users.id", ondelete="cascade"),
        nullable=False,
    )
    provider: Mapped[str]
    provider_account_id: Mapped[str]

    user: Mapped[User] = relationship(
        back_populates="accounts",
        lazy="selectin",
        init=False,
    )

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:

# Copy models from examples/alchemy/auth_models.py to your project
# Then import from your own code:
from myapp.models import User, Account, Session, OAuthState

This gives you full control to customize the models for your application.

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.6.3.tar.gz (6.2 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.6.3-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: belgie_alchemy-0.6.3.tar.gz
  • Upload date:
  • Size: 6.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","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.6.3.tar.gz
Algorithm Hash digest
SHA256 31d1184f82e80d3376639a693135d6c5cfa899c31c30c411eb8d032daaa002ca
MD5 092af98fd2a5b90823f14adfb3ba4d84
BLAKE2b-256 7323ce7e03d5d74e6c6156a5bbf8e2053e7ef820941af2de11e541068b538c54

See more details on using hashes here.

File details

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

File metadata

  • Download URL: belgie_alchemy-0.6.3-py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","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.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 35c1c1e65c05b5b4058d974f33f9788599dc0d3a62cb78c463617bff95c2b142
MD5 13e0396118b3b1ea9d32b74fe9e8d634
BLAKE2b-256 3a87a3c4cd1ebb68c1e515d10d1938d941a4d6abf50180330bc11ad69b0e21dc

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