Proper way to make a TypeDecorator around Enum?

219 views
Skip to first unread message

Nikolaj

unread,
Aug 9, 2010, 8:24:44 AM8/9/10
to sqlalchemy
I'm on MySQL and trying to create a type that stores enums as
VARCHAR(255). However, the following code still issues an ENUM() DDL.
What am I doing wrong here?

class StringEnum(types.TypeDecorator):
impl = types.Enum

def __init__(self, *args, **kwargs):
super(StringEnum, self).__init__(*args, native_enum=False,
**kwargs)
self.length = 255

Michael Bayer

unread,
Aug 9, 2010, 9:56:56 AM8/9/10
to sqlal...@googlegroups.com
TypeDecorator doesn't affect the decorated type via subclassing, it affects it via the "impl" attribute, hence "decorates". So you say self.impl = Enum(...). You don't need to use TypeDecorator here if all you want to do is create an Enum with default arguments, just subclass Enum directly.

> --
> You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
> To post to this group, send email to sqlal...@googlegroups.com.
> To unsubscribe from this group, send email to sqlalchemy+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
>

Nikolaj

unread,
Aug 9, 2010, 10:32:49 AM8/9/10
to sqlalchemy
It makes sense to subclass the type directly if I'm not messing with
result or bind values. This works:

class StringEnum(types.Enum):
def __init__(self, *args, **kwargs):
super(StringEnum, self).__init__(*args, native_enum=False,
**kwargs)
self.length = 255

I'm still curious why the TypeDecorator I pasted previously doesn't
work though. The superconstructor does the self.impl =
self.__class__.impl(...) anyway so I figured this was the better
approach than calling self.impl = Enum(...).

Michael Bayer

unread,
Aug 9, 2010, 10:38:38 AM8/9/10
to sqlal...@googlegroups.com

On Aug 9, 2010, at 10:32 AM, Nikolaj wrote:

> It makes sense to subclass the type directly if I'm not messing with
> result or bind values. This works:
>
> class StringEnum(types.Enum):
> def __init__(self, *args, **kwargs):
> super(StringEnum, self).__init__(*args, native_enum=False,
> **kwargs)
> self.length = 255
>
> I'm still curious why the TypeDecorator I pasted previously doesn't
> work though. The superconstructor does the self.impl =
> self.__class__.impl(...) anyway so I figured this was the better
> approach than calling self.impl = Enum(...).

its pulling off the "types.Enum" callable from self.__class__ (which actually seems unnecessary here, self.impl should be fine), then calling that with the given arguments. So it should be setting up the native_enum=False part at least. The length attribute though would need to be set on impl. There's no reason we can't make "length" an actual argument of Enum, though, just to help with this kind of thing (I'm assuming that you're planning on adding additional elements to the enum in the future and you don't want to have to alter the column, correct ?).

Nikolaj

unread,
Aug 9, 2010, 10:52:41 AM8/9/10
to sqlalchemy
On Aug 9, 3:38 pm, Michael Bayer <mike...@zzzcomputing.com> wrote:

> its pulling off the "types.Enum" callable from self.__class__ (which actually seems unnecessary here, self.impl should be fine), then calling that with the given arguments.  So it should be setting up the native_enum=False part at least.   The length attribute though would need to be set on impl.        There's no reason we can't make "length" an actual argument of Enum, though, just to help with this kind of thing (I'm assuming that you're planning on adding additional elements to the enum in the future and you don't want to have to alter the column, correct ?).

Correct. About the self.length thing, I simply misread __getattr__ on
TypeDecorator as __setattr__. I've included my test below just for
completness' sake to show that everything works as expected.

from sqlalchemy import create_engine, Column, types
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = scoped_session(sessionmaker(bind=engine))

class StringEnum(types.TypeDecorator):
impl = types.Enum

def __init__(self, *args, **kwargs):
super(StringEnum, self).__init__(*args, native_enum=False,
**kwargs)
self.impl.length = 255

class Item(Base):
__tablename__ = 'items'
name = Column(types.String, primary_key=True)
option = Column(StringEnum(('foo', 'bar')), nullable=True)

Base.metadata.create_all()
Reply all
Reply to author
Forward
0 new messages