SQLAlchemy building blocks for Belgie
Project description
belgie-alchemy
SQLAlchemy 2.0 utilities for Belgie.
Overview
belgie-alchemy provides the BelgieAdapter and auth model mixins 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_attimestamps - 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 (
datetime→DateTimeUTC) - 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_atset automatically on insertupdated_atauto-updates on row changesdeleted_atfor soft deletionmark_deleted()method to setdeleted_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
CITEXTvariants for case-insensitiveemail,provider, andprovider_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
- Building blocks, not frameworks - You own your models completely
- Sensible defaults - UTC datetimes, UUIDs, timestamps by default
- Dataclass-friendly - Clean instantiation and repr
- Dialect-aware - Use the best type for each database
- 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file belgie_alchemy-0.8.0.tar.gz.
File metadata
- Download URL: belgie_alchemy-0.8.0.tar.gz
- Upload date:
- Size: 8.4 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d0cf174efaefde54642eb3f8dd90cac74e70b9f6cc5baabd16743d26f32e178
|
|
| MD5 |
f2db464dcbd1a0d9d225c73b2a47d655
|
|
| BLAKE2b-256 |
6e44142fb4ef4eab8ffbc791e8dad6d88a45563cd42f3fa247e1b2ffcf53278f
|
File details
Details for the file belgie_alchemy-0.8.0-py3-none-any.whl.
File metadata
- Download URL: belgie_alchemy-0.8.0-py3-none-any.whl
- Upload date:
- Size: 14.3 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b975a082addbef07f51507e852f437a6207e9b6c7369cc311ed278fac3ff103a
|
|
| MD5 |
edd7cd199fa8ff96b0a310ca7409f2d8
|
|
| BLAKE2b-256 |
a0056c189a8a2540c765fe1a5d20288885ffd9510e522b7ce9129c722c5a36ac
|