Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble when using alembic with sqlalchemy_utils

In my sqlalchemy model i use sqlalchemy_utils' choicetype:

id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.Integer, nullable=True)
level = db.Column(mytypes.types.ChoiceType(LEVEL))

I did everything as described here http://alembic.readthedocs.org/en/latest/autogenerate.html#autogen-module-prefix. In my model i imported choicetype from my module mytypes.types:

from sqlalchemy_utils.types.choice import ChoiceType

, in alembic/env.py i added context

context.configure(
    connection=connection,
    target_metadata=target_metadata,
    user_module_prefix="mytypes.types."
    # ...
)

, and in the script.py.mako

import mytypes.types

.The problem is when i am making revision of my model, i getting something
like this

from alembic import op
import sqlalchemy as sa
import mytypes.types

def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('logging', sa.Column('level', mytypes.types.ChoiceType(length=255), nullable=True))
### end Alembic commands ###

Why alembic didn't pass "LEVEL" argument to choicetype and why it passed length=255 instead?

like image 806
Андрей Сорочинский Avatar asked May 08 '15 20:05

Андрей Сорочинский


2 Answers

I fixed it by manually change this mytypes.types.ChoiceType(length=255) to mytypes.types.ChoiceType(MyEnum) and import it.

like image 42
Ryang MinHo Avatar answered Sep 20 '22 14:09

Ryang MinHo


I've come up with a more automated solution for this problem. In my env.py, in the function run_migrations_online(), I've added a custom render that handles the sqlalchemy_utils types. I've only tested with ChoiceType and UUIDType, but they work for me.

So in env.py and the custom render function:

  def render_item(type_, obj, autogen_context):
        """Apply custom rendering for selected items."""

        if type_ == "type" and obj.__class__.__module__.startswith("sqlalchemy_utils."):
            autogen_context.imports.add(f"import {obj.__class__.__module__}")
            if hasattr(obj, "choices"):
                return f"{obj.__class__.__module__}.{obj.__class__.__name__}(choices={obj.choices})"
            else:
                return f"{obj.__class__.__module__}.{obj.__class__.__name__}()"

        # default rendering for other objects
        return False


Then I modified the existing context configure to use it:

   with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=target_metadata,
            render_item=render_item,
            process_revision_directives=process_revision_directives,
            **current_app.extensions["migrate"].configure_args,
        )

This means when I run manage.py db migrate with a new sqlalchemy_utils it correctly imports the module and adds the type to the migrarions. I've add special handling for the choices attributes of the ChoiceType. It's possible this will need to be extended for more types/attributes.

like image 150
John-Paul Stanford Avatar answered Sep 19 '22 14:09

John-Paul Stanford