Skip to main content

Table column annotations for SQLAlchemy

Table column annotations for SQLAlchemy

Instead of this:

from datetime import UTC, datetime
from uuid import UUID, uuid4

import sqlalchemy as sa
from sqlalchemy.orm import Mapped, mapped_column


class User(Base):
    id: Mapped[int] = mapped_column(sa.Integer(), sa.Identity(always=True), primary_key=True)
    email: Mapped[str] = mapped_column(sa.Text(), unique=True, default='')
    confirmed: Mapped[bool] = mapped_column(sa.Boolean(), default=False)
    connection_id: Mapped[int | None] = mapped_column(sa.BigInteger(), index=True)
    token: Mapped[UUID | None] = mapped_column(sa.Uuid(), index=True, default=uuid4)
    created_at: Mapped[datetime] = mapped_column(sa.DateTime(timezone=True), default=lambda: datetime.now(tz=UTC))

you can declare your SQLAlchemy models like so:

import sqlalchemy_annotations as saa
from sqlalchemy.orm import Mapped


class User(Base):
    id: Mapped[saa.IntegerPKColumn]
    email: Mapped[saa.TextUniqueColumn]
    confirmed: Mapped[saa.BooleanDefaultFalseColumn]
    connection_id: Mapped[saa.BigIntegerIndexColumn | None]
    token: Mapped[saa.UUIDIndexColumn | None]
    created_at: Mapped[saa.DateTimeDefaultUtcNowColumn]

What are these IntegerPKColumn, BigIntegerIndexColumn, BooleanDefaultFalseColumn and other ones?
These are just type aliases created with typing.Annotated function that adds context-specific metadata to a type, so that you don't need to specify all these extra assignments to mapped_column function

Links

Examples

Integers (INTEGER, BIGINT, SERIAL, BIGSERIAL)

  • IntegerPKColumn / BigIntegerPKColumn – INTEGER / BIGINT, GENERATED ALWAYS AS IDENTITY, PRIMARY KEY
  • SerialPKColumn / BigSerialPKColumn – SERIAL / BIGSERIAL, PRIMARY KEY
  • IntegerColumn / BigIntegerColumn – INTEGER / BIGINT
  • IntegerIndexColumn / BigIntegerIndexColumn – INTEGER / BIGINT, INDEX
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import IntegerColumn, IntegerIndexColumn, IntegerPKColumn


class Model(Base):
    id: Mapped[IntegerPKColumn]
    # equivalent to
    # id: Mapped[int] = mapped_column(sa.Integer(), Identity(always=True), primary_key=True)

    number: Mapped[IntegerColumn]
    # equivalent to
    # number: Mapped[int] = mapped_column(sa.Integer())

    uid: Mapped[IntegerIndexColumn]
    # equivalent to
    # uid: Mapped[int] = mapped_column(sa.Integer(), index=True)

Strings (TEXT)

  • TextPKColumn – TEXT, PRIMARY KEY
  • TextColumn – TEXT with default=''
  • TextUniqueColumn – TEXT, UNIQUE
  • TextIndexColumn – TEXT, INDEX
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import TextColumn, TextIndexColumn, TextPKColumn, TextUniqueColumn


class Model(Base):
    id: Mapped[TextPKColumn]
    # equivalent to
    # id: Mapped[str] = mapped_column(sa.Text(), primary_key=True)

    name: Mapped[TextColumn]
    # equivalent to
    # name: Mapped[str] = mapped_column(sa.Text(), default='')

    email: Mapped[TextUniqueColumn]
    # equivalent to
    # email: Mapped[str] = mapped_column(sa.Text(), unique=True)

    ext_id: Mapped[TextIndexColumn]
    # equivalent to
    # ext_id: Mapped[str] = mapped_column(sa.Text(), index=True)

UUID (UUID)

  • UUIDPKColumn – UUID, PRIMARY KEY with default=uuid4
  • UUIDIndexColumn – UUID, INDEX with default=uuid4
  • UUIDColumn – UUID with default=uuid4
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import UUIDColumn, UUIDIndexColumn, UUIDPKColumn


class Model(Base):
    id: Mapped[UUIDPKColumn]
    # equivalent to
    # id: Mapped[str] = mapped_column(sa.Uuid(), primary_key=True, default=uuid4)

    token: Mapped[UUIDIndexColumn]
    # equivalent to
    # token: Mapped[str] = mapped_column(sa.Uuid(), index=True, default=uuid4)

    ext_id: Mapped[UUIDColumn]
    # equivalent to
    # ext_id: Mapped[str] = mapped_column(sa.Uuid(), default=uuid4)

Date, Time, Datetime (DATE, TIME, TIMESTAMP)

  • DateColumn – DATE
  • TimeColumn – TIME WITH TIME ZONE
  • TimeWOTimezoneColumn – TIME WITHOUT TIME ZONE
  • DateTimeColumn – TIMESTAMP WITH TIME ZONE
  • DateTimeWOTimezoneColumn – TIMESTAMP WITHOUT TIME ZONE
  • DateTimeDefaultUtcNowColumn – TIMESTAMP WITH TIME ZONE with default=lambda: datetime.now(tz=UTC)
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import (
    DateColumn,
    DateTimeColumn,
    DateTimeDefaultUtcNowColumn,
    DateTimeWOTimezoneColumn,
    TimeColumn,
    TimeWOTimezoneColumn,
)

class Model(Base):
    ...

    birthday: Mapped[DateColumn]
    # equivalent to
    # birthday: Mapped[date] = mapped_column(sa.Date())

    start_at: Mapped[TimeColumn]
    # equivalent to
    # start_at: Mapped[time] = mapped_column(sa.Time(timezone=True))

    notify_at: Mapped[TimeWOTimezoneColumn]
    # equivalent to
    # notify_at: Mapped[time] = mapped_column(sa.Time(timezone=False))

    approved_at: Mapped[DateTimeColumn]
    # equivalent to
    # approved_at: Mapped[datetime] = mapped_column(sa.DateTime(timezone=True))

    failed_at: Mapped[DateTimeWOTimezoneColumn]
    # equivalent to
    # failed_at: Mapped[datetime] = mapped_column(sa.DateTime(timezone=False))

    created_at: Mapped[DateTimeDefaultUtcNowColumn]
    # equivalent to
    # created_at: Mapped[datetime] = mapped_column(sa.DateTime(timezone=True), default=lambda: datetime.now(tz=UTC))

Booleans (BOOLEAN)

  • BooleanColumn – BOOLEAN
  • BooleanDefaultFalseColumn – BOOLEAN with default=False
  • BooleanDefaultTrueColumn – BOOLEAN with default=True
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import BooleanColumn, BooleanDefaultFalseColumn, BooleanDefaultTrueColumn


class Model(Base):
    ...

    is_active: Mapped[BooleanColumn]
    # equivalent to
    # is_active: Mapped[bool] = mapped_column(Boolean())

    is_confirmed: Mapped[BooleanDefaultFalseColumn]
    # equivalent to
    # is_confirmed: Mapped[bool] = mapped_column(Boolean(), default=False)

    is_visible: Mapped[BooleanDefaultTrueColumn]
    # equivalent to
    # is_visible: Mapped[bool] = mapped_column(Boolean(), default=True)

String enums (VARCHAR)

This annotation will not create a native enum
The column will be VARCHAR (without length limit) so that you can easily update your enum

  • BooleanColumn – BOOLEAN
  • BooleanDefaultFalseColumn – BOOLEAN with default=False
  • BooleanDefaultTrueColumn – BOOLEAN with default=True
from enum import StrEnum

from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import StrEnumColumn


class UserStatus(StrEnum):
    REGISTERED = 'REGISTERED'
    ACTIVATED = 'ACTIVATED'
    BANNED = 'BANNED'


class Model(Base):
    ...

    status: Mapped[StrEnumColumn[UserStatus]]
    # equivalent to
    # status: Mapped[UserStatus] = mapped_column(Enum(UserStatus, native_enum=False, length=None))

A note on contribution

This is opensource project
So if you find something that you can add of fix, feel free to open an issue and raise a pull request

Thank you!

Metadata

Release files for sqlalchemy-annotations 0.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sqlalchemy-annotations 0.0.2
File Size Uploaded
sqlalchemy_annotations-0.0.2.tar.gz 7.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sqlalchemy-annotations 0.0.2
File Interpreter ABI Platform
sqlalchemy_annotations-0.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 13.9 kB

Release files / sqlalchemy_annotations-0.0.2.tar.gz

Download URL sqlalchemy_annotations-0.0.2.tar.gz
Size 7.0 kB
Tags Source
SHA-256 checksum
How to use checksums
74533ed688a82e5b0d01b3909512e898be823ab9f19c4404a2c1ebcacf93d319
BLAKE2b-256 checksum
How to use checksums
b0d80f9f62e1485c5c59ea1a9a34115410e9cf8915d9f021f695574198e531f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.5

Release files / sqlalchemy_annotations-0.0.2-py3-none-any.whl

Download URL sqlalchemy_annotations-0.0.2-py3-none-any.whl
Size 7.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
12416461a35c13e7a86ab4ff554a09c7374981f87c2cdc73b45e0c6687d69bcd
BLAKE2b-256 checksum
How to use checksums
7062c80555e64e09af782c6494eca6ac590fdd3998402b4916a3ee9320fbfb2c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.5

Release history Release notifications | RSS feed

This release

0.0.2 This release

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page