Skip to content

Instantly share code, notes, and snippets.

@hasansezertasan
Created May 12, 2024 01:12
Show Gist options
  • Save hasansezertasan/691a7ef67cc79ea669ff76d168503235 to your computer and use it in GitHub Desktop.
Save hasansezertasan/691a7ef67cc79ea669ff76d168503235 to your computer and use it in GitHub Desktop.
How to use SQLAlchemy with numerical enums?
"""
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
from sqlalchemy import Integer, TypeDecorator, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from starlette.applications import Starlette
from starlette_admin.contrib.sqla import Admin, 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"),
],
),
]
with engine.connect() as conn:
person = Person(name="John Doe", gender=1)
Base.metadata.create_all(engine)
app = Starlette()
admin = Admin(engine, title="SQLAlchemy Numerical Enum Example")
admin.add_view(PersonView(Person))
admin.mount_to(app)
starlette-admin
sqlalchemy
uvicorn
@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