Skip to main content

Table column annotations for SQLAlchemy (v2)

Project description

Table column annotations for SQLAlchemy

Table column annotations for SQLAlchemy (v2)

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

Examples

Integers (INTEGER, BIGINT, SERIAL, BIGSERIAL)

  • IntegerPKColumn / BigIntegerPKColumnINTEGER / BIGINT, GENERATED ALWAYS AS IDENTITY, PRIMARY KEY
  • SerialPKColumn / BigSerialPKColumnSERIAL / BIGSERIAL, PRIMARY KEY
  • IntegerColumn / BigIntegerColumnINTEGER / BIGINT
  • IntegerIndexColumn / BigIntegerIndexColumnINTEGER / 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)

  • TextPKColumnTEXT, PRIMARY KEY
  • TextColumnTEXT with default=''
  • TextUniqueColumnTEXT, UNIQUE
  • TextIndexColumnTEXT, INDEX
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import TextColumn, TextIndexColumn, TextPKColumn, TextUniqueColumn


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

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

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

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

UUID (UUID)

  • UUIDPKColumnUUID, PRIMARY KEY with default=uuid4
  • UUIDIndexColumnUUID, INDEX with default=uuid4
  • UUIDColumnUUID with default=uuid4
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import UUIDColumn, UUIDIndexColumn, UUIDPKColumn


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

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

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

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

  • DateColumnDATE
  • TimeColumnTIME WITH TIME ZONE
  • TimeWOTimezoneColumnTIME WITHOUT TIME ZONE
  • DateTimeColumnTIMESTAMP WITH TIME ZONE
  • DateTimeWOTimezoneColumnTIMESTAMP WITHOUT TIME ZONE
  • DateTimeDefaultUtcNowColumnTIMESTAMP 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]
    # birthday: Mapped[date] = mapped_column(sa.Date())

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

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

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

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

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

Booleans (BOOLEAN)

  • BooleanColumnBOOLEAN
  • BooleanDefaultFalseColumnBOOLEAN with default=False
  • BooleanDefaultTrueColumnBOOLEAN with default=True
from sqlalchemy.orm import Mapped
from sqlalchemy_annotations import BooleanColumn, BooleanDefaultFalseColumn, BooleanDefaultTrueColumn


class Model(Base):
    ...
    is_active: Mapped[BooleanColumn]
    # is_active: Mapped[bool] = mapped_column(Boolean())

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

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

More complex example (and a note on contribution)

If you need to more functionality and flexibility, you still can use regular SQLAlchemy functionality:

from enum import StrEnum

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


class UserKind(StrEnum):
    GUEST = 'GUEST'
    MODERATOR = 'MODERATOR'
    ADMIN = 'ADMIN'


class User(Base):
    id: Mapped[int] = mapped_column(sa.BigInteger(), sa.Identity(always=False), primary_key=True)
    kind: Mapped[UserKind] = mapped_column(
        sa.Enum(UserKind, native_enum=False, length=None),
        default=UserKind.GUEST,
    )

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

Good luck!

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

sqlalchemy_annotations-0.0.1.tar.gz (5.2 kB view details)

Uploaded Source

Built Distribution

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

sqlalchemy_annotations-0.0.1-py3-none-any.whl (6.3 kB view details)

Uploaded Python 3

File details

Details for the file sqlalchemy_annotations-0.0.1.tar.gz.

File metadata

File hashes

Hashes for sqlalchemy_annotations-0.0.1.tar.gz
Algorithm Hash digest
SHA256 e0e65f26ce3c3535bc361a644965a404245fdbab26cb6f189670c8790f1e8cc4
MD5 c3a79c82498a9842d10531fdecaac0cb
BLAKE2b-256 41df65993bd7d321d2fbd1cedf8a79d519a12b409f26bf5734c80bd309204080

See more details on using hashes here.

File details

Details for the file sqlalchemy_annotations-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for sqlalchemy_annotations-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 df3e4da22d1426755ab4f1c4dbd1315701f0f43199896b81224efa250c64aca4
MD5 ecb192cfe9682a93a3c6493a1d881a4c
BLAKE2b-256 db06f832a050d1e37265a38c64976999a69ad8f38f2206828324979c20736d7c

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