This does not change a lot. Judging by the logs I still believe no create statement is issued:
(env)jhnwsk@master-blaster:~/_development/$ alembic upgrade head
INFO [sqlalchemy.engine.base.Engine] select version()
INFO [sqlalchemy.engine.base.Engine] {}
DEBUG [sqlalchemy.engine.base.Engine] Col ('version',)
DEBUG
[sqlalchemy.engine.base.Engine] Row (u'PostgreSQL 9.1.9 on
x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu/Linaro 4.6.3-1ubuntu5)
4.6.3, 64-bit',)
INFO [sqlalchemy.engine.base.Engine] select current_schema()
INFO [sqlalchemy.engine.base.Engine] {}
DEBUG [sqlalchemy.engine.base.Engine] Col ('current_schema',)
DEBUG [sqlalchemy.engine.base.Engine] Row (u'public',)
INFO [sqlalchemy.engine.base.Engine] BEGIN (implicit)
DEBUG [txn.140264917944064] new transaction
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO
[sqlalchemy.engine.base.Engine] select relname from pg_class c join
pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema()
and relname=%(name)s
INFO [sqlalchemy.engine.base.Engine] {'name': u'alembic_version'}
DEBUG [sqlalchemy.engine.base.Engine] Col ('relname',)
INFO [sqlalchemy.engine.base.Engine]
CREATE TABLE alembic_version (
version_num VARCHAR(32) NOT NULL
)
INFO [sqlalchemy.engine.base.Engine] {}
INFO [sqlalchemy.engine.base.Engine] SELECT alembic_version.version_num
FROM alembic_version
INFO [sqlalchemy.engine.base.Engine] {}
DEBUG [sqlalchemy.engine.base.Engine] Col ('version_num',)
INFO [alembic.migration] Running upgrade None -> 41ca36462c98, My very first alembic migration
INFO [sqlalchemy.engine.base.Engine] ALTER TABLE clients ADD COLUMN came_from ck_client_came_from NOT NULL
INFO [sqlalchemy.engine.base.Engine] {}
INFO [sqlalchemy.engine.base.Engine] ROLLBACK
Traceback (most recent call last):
...
...
...
sqlalchemy.exc.ProgrammingError: (ProgrammingError) type "ck_client_came_from" does not exist
LINE 1: ALTER TABLE clients ADD COLUMN came_from ck_client_came_from...
^
'ALTER TABLE clients ADD COLUMN came_from ck_client_came_from NOT NULL' {}
My
problem is that upon calling DeclEnumType.create() and in consequence
self.dialect_impl(bind.dialect), the implied type of the DeclEnumType
gets hidden away by the actual DeclEnum type. Thus, the create statement for the Enum type is never
emitted.
I managed to "FIX" this by overriding the create method on the DeclEnumType like so:
class DeclEnumType(SchemaType, TypeDecorator):
def __init__(self, enum):
self.enum = enum
self.impl = Enum(
*enum.values(),
name="ck%s" % re.sub(
'([A-Z])',
lambda m: "_" + m.group(1).lower(),
enum.__name__)
)
...
...
def create(self, bind=None, checkfirst=False):
"""Issue CREATE ddl for this type, if applicable."""
super(DeclEnumType, self).create(bind, checkfirst)
t = self.dialect_impl(bind.dialect)
if t.impl.__class__ is not self.__class__ and isinstance(t, SchemaType):
t.impl.create(bind=bind, checkfirst=checkfirst)
This way my ```t``` is the {Enum} ck_client_came_from type (I'm guessing the implied type for the DeclEnum abstraction) and the create statement gets emitted like so:
(env)jhnwsk@master-blaster:~/_development/$ alembic upgrade head
INFO [sqlalchemy.engine.base.Engine] select version()
INFO [sqlalchemy.engine.base.Engine] {}
DEBUG [sqlalchemy.engine.base.Engine] Col ('version',)
DEBUG
[sqlalchemy.engine.base.Engine] Row (u'PostgreSQL 9.1.9 on
x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu/Linaro 4.6.3-1ubuntu5)
4.6.3, 64-bit',)
INFO [sqlalchemy.engine.base.Engine] select current_schema()
INFO [sqlalchemy.engine.base.Engine] {}
DEBUG [sqlalchemy.engine.base.Engine] Col ('current_schema',)
DEBUG [sqlalchemy.engine.base.Engine] Row (u'public',)
INFO [sqlalchemy.engine.base.Engine] BEGIN (implicit)
DEBUG [txn.140612540696320] new transaction
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO
[sqlalchemy.engine.base.Engine] select relname from pg_class c join
pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema()
and relname=%(name)s
INFO [sqlalchemy.engine.base.Engine] {'name': u'alembic_version'}
DEBUG [sqlalchemy.engine.base.Engine] Col ('relname',)
INFO [sqlalchemy.engine.base.Engine]
CREATE TABLE alembic_version (
version_num VARCHAR(32) NOT NULL
)
INFO [sqlalchemy.engine.base.Engine] {}
INFO [sqlalchemy.engine.base.Engine] SELECT alembic_version.version_num
FROM alembic_version
INFO [sqlalchemy.engine.base.Engine] {}
DEBUG [sqlalchemy.engine.base.Engine] Col ('version_num',)
INFO [alembic.migration] Running upgrade None -> 41ca36462c98, My very first alembic migration
INFO [sqlalchemy.engine.base.Engine] CREATE TYPE ck_client_came_from AS ENUM ('A','C','B','E','D')
INFO [sqlalchemy.engine.base.Engine] {}
INFO [sqlalchemy.engine.base.Engine] ALTER TABLE clients ADD COLUMN came_from ck_client_came_from DEFAULT 'A' NOT NULL
INFO [sqlalchemy.engine.base.Engine] {}
INFO [sqlalchemy.engine.base.Engine] INSERT INTO alembic_version (version_num) VALUES ('41ca36462c98')
INFO [sqlalchemy.engine.base.Engine] {}
Hooray!
There's two questions I have
1.
Does what I did make any sense whatsoever? Is the Declarative Enum from the
recipe meant to be created explicitly this way? Your code is very
descriptive and I THNIK I get the concept of implied types but honestly I
am kind of a dog in the fog with my "fix" ;)
2. I can get the exact
same effect if I just tell my migration to use my ScopedSession and Base
and say: Base.metadata.create_all() in the run online migrations
routine. Is that the preferred way to run migrations online when using
the (oh-so-awesome) ORM from SQLAlchemy?
Thanks for you patience, I'm kind of new to this :)
cheers,
John