Skip to main content

Table column annotations for SQLAlchemy

Project description

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 / 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]
    # 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)

  • 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]
    # 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)

  • 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]
    # 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)

  • 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]
    # 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

  • BooleanColumnBOOLEAN
  • BooleanDefaultFalseColumnBOOLEAN with default=False
  • BooleanDefaultTrueColumnBOOLEAN 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!

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

Uploaded Python 3

File details

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

File metadata

File hashes

Hashes for sqlalchemy_annotations-0.0.2.tar.gz
Algorithm Hash digest
SHA256 74533ed688a82e5b0d01b3909512e898be823ab9f19c4404a2c1ebcacf93d319
MD5 5096fdc290818322fa60cb9a8461720c
BLAKE2b-256 b0d80f9f62e1485c5c59ea1a9a34115410e9cf8915d9f021f695574198e531f1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for sqlalchemy_annotations-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 12416461a35c13e7a86ab4ff554a09c7374981f87c2cdc73b45e0c6687d69bcd
MD5 152082f00147428323c088ab6d26367b
BLAKE2b-256 7062c80555e64e09af782c6494eca6ac590fdd3998402b4916a3ee9320fbfb2c

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