I am using sqlalchemy as a readable way to model my database, I'm only interested in generating the database definition for several engines from my model.
Some of the columns in my tables have type Enum
, which works fine in engines such as MySQL, since it has native enum support. However for SQL Server, it generates the column as VARCHAR
and sets a constraint to check the values are within the expected enum values I specify.
I'd like to replace this alternative with a numeric based fallback, so that the column type is actually numeric and the constraint checks the numeric values are within the range of the enum size (assumes sequential values starting with 0).
I have tried creating a TypeDecorator
with Enum
as impl
, but this was not enough or I did not know how to make it work. I also tried to just copy the code for the Boolean type and mix it with the Enum type to create my own type, but it seems that database compiler support is required too.
Is there a way in which I can achieve this without having to patch sqlalchemy itself?
Note that I am not interested in querying the database with python, after it's generated, I'm done, so that might simplify, perhaps.
Enum is a class within the sqlalchemy. types module of the SQLAlchemy project.
PickleType. Holds Python objects, which are serialized using pickle.
From SQLAlchemy docs: nullable – If set to the default of True, indicates the column will be rendered as allowing NULL, else it's rendered as NOT NULL. This parameter is only used when issuing CREATE TABLE statements.
SQLite does support the enum data type, but it is very compact to store the data.
Here's what you need:
import sqlalchemy as sa
class IntEnum(sa.types.TypeDecorator):
impl = sa.Integer
def __init__(self, enumtype, *args, **kwargs):
super().__init__(*args, **kwargs)
self._enumtype = enumtype
def process_bind_param(self, value, dialect):
return value.value
def process_result_value(self, value, dialect):
return self._enumtype(value)
And then you use it like this:
from enum import Enum
from sqlalchemy.ext.declarative import declarative_base
class MyEnum(Enum):
one = 1
two = 2
three = 3
engine = sa.create_engine('sqlite:///:memory:')
session = sa.orm.sessionmaker(bind=engine)()
Base = declarative_base()
class Stuff(Base):
__tablename__ = 'stuff'
id = sa.Column('id', sa.Integer, primary_key=True)
thing = sa.Column('num', IntEnum(MyEnum))
Base.metadata.create_all(engine)
session.add(Stuff(thing=MyEnum.one))
session.add(Stuff(thing=MyEnum.two))
session.add(Stuff(thing=MyEnum.three))
session.commit()
engine.execute(sa.text('insert into stuff values(4, 2);'))
for thing in session.query(Stuff):
print(thing.id, thing.thing)
Really your only problem is that impl
needed to be a sa.Integer
, as that's what's actually backing the enum, not enum.Enum
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With