Skip to content

Instantly share code, notes, and snippets.

@hasansezertasan
Last active March 6, 2026 01:59
Show Gist options
  • Select an option

  • Save hasansezertasan/691a7ef67cc79ea669ff76d168503235 to your computer and use it in GitHub Desktop.

Select an option

Save hasansezertasan/691a7ef67cc79ea669ff76d168503235 to your computer and use it in GitHub Desktop.
How to use SQLAlchemy with numerical enums?
# /// script
# requires-python = ">=3.14"
# dependencies = [
# "sqlalchemy==2.0.48",
# "starlette-admin==0.16.0",
# "uvicorn==0.41.0",
# ]
# ///
"""
How to use SQLAlchemy with numerical enums?
`IntEnum` class is taken from this article: [Using Python enums in SQLAlchemy models](https://michaelcho.me/article/using-python-enums-in-sqlalchemy-models/), all credits to the author.
"""
import datetime
import enum
import uvicorn
from sqlalchemy import Integer
from sqlalchemy import TypeDecorator
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from starlette.applications import Starlette
from starlette_admin.contrib.sqla import Admin
from starlette_admin.contrib.sqla import ModelView
from starlette_admin.fields import EnumField
engine = create_engine(
"sqlite:///db.sqlite3", connect_args={"check_same_thread": False}
)
class IntEnum(TypeDecorator):
"""
Enables passing in a Python enum and storing the enum's *value* in the db.
The default would have stored the enum's *name* (ie the string).
"""
impl = Integer
def __init__(self, enumtype, *args, **kwargs):
super(IntEnum, self).__init__(*args, **kwargs)
self._enumtype = enumtype
def process_bind_param(self, value, dialect):
if isinstance(value, int):
return value
return value.value
def process_result_value(self, value, dialect):
return self._enumtype(value)
class Gender(enum.IntEnum):
MALE = 1
FEMALE = 2
PREFER_NOT_TO_SAY = 3
class Base(DeclarativeBase):
pass
class Mixin:
id: Mapped[int] = mapped_column(
primary_key=True,
autoincrement=True,
index=True,
unique=True,
)
date_created: Mapped[datetime.datetime] = mapped_column(
default=datetime.datetime.utcnow,
index=True,
)
date_updated: Mapped[datetime.datetime] = mapped_column(
default=datetime.datetime.utcnow,
onupdate=datetime.datetime.utcnow,
index=True,
)
class Person(Base, Mixin):
__tablename__ = "person"
name: Mapped[str]
gender: Mapped[Gender] = mapped_column(IntEnum(Gender))
class PersonView(ModelView):
fields = [
"name",
EnumField(
name="gender",
coerce=int,
choices=[
(1, "Male"),
(2, "Female"),
(3, "Perefer not to say"),
],
),
]
def main() -> None:
with engine.connect() as conn:
person = Person(name="John Doe", gender=1)
conn.add(person)
conn.commit()
Base.metadata.create_all(engine)
app = Starlette()
admin = Admin(engine, title="SQLAlchemy Numerical Enum Example")
admin.add_view(PersonView(Person))
admin.mount_to(app)
uvicorn.run(app, host="127.0.0.1", port=8000)
if __name__ == "__main__":
main()
@yalin
Copy link

yalin commented May 14, 2024

for the ones want to do with Column, I did this way;

gender = Column(IntEnum(Gender), name="gender", nullable=False)

note: nullable is optional

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment