Alembic Autogenerate Enums

raw JSON →
0.1.2 verified Thu Apr 16 auth: no python

alembic-autogenerate-enums is a Python library that provides an Alembic hook to automatically handle the upgrading and downgrading of enum values in database migrations. It simplifies managing changes to SQLAlchemy `Enum` types, particularly when adding, removing, or reordering enum members. The current version is 0.1.2, and it appears to have a low but steady release cadence, focusing on stability and compatibility with older SQLAlchemy versions.

pip install alembic-autogenerate-enums
error AttributeError: 'EnvironmentContext' object has no attribute 'api'
cause The `api` argument was not correctly passed to `context.configure` in `env.py`, or a typo exists.
fix
Ensure your env.py has api = create_api() and then context.configure(..., api=api, ...).
error Alembic `autogenerate` command does not detect enum value changes, even after setting up the library.
cause The library's `ColumnEnum` type is not being used in the model definition, or the `api` hook is not correctly integrated into `env.py`.
fix
Verify that your SQLAlchemy Enum columns are defined using alembic_autogenerate_enums.ColumnEnum (with a values_callable) and that the api hook is correctly configured in env.py by passing api=api to context.configure.
error TypeError: 'UserStatus' object is not iterable
cause This error often occurs when `ColumnEnum` is not provided with a `values_callable` or if the `values_callable` itself is incorrectly defined, preventing the enum values from being properly iterated or compared.
fix
Ensure your ColumnEnum definition includes values_callable=lambda x: [m.value for m in x] (or a similar function that returns a list of string values) to correctly extract the enum members.
gotcha The autogenerate hook will not function unless `api=api` is explicitly passed to `alembic.context.configure()` in your `env.py` file. Forgetting this step is a common setup error.
fix Ensure your `env.py` has `api = create_api()` and then `context.configure(..., api=api, ...)`.
gotcha The specific enum value detection and migration capabilities are tied to using the library's `ColumnEnum` type in your SQLAlchemy models. Standard `SQLAlchemy.Enum` types might not be fully supported by the autogeneration hook for value changes.
fix Replace `SQLAlchemy.Enum` with `alembic_autogenerate_enums.ColumnEnum` in your models for enums you want to manage automatically.
gotcha If you have existing custom logic in `process_revision_directives` within `env.py`, ensure the `alembic-autogenerate-enums` API is called correctly and doesn't interfere with or override your custom processing.
fix Review the order of operations and ensure the `api` hook's `process_revision_directives` method is applied either before or after your custom directives as appropriate, or integrated by wrapping the existing `process_revision_directives` with the `api.process_revision_directives`.

To enable automatic enum migration, you need to integrate `create_api()` into your `alembic/env.py` file by passing the `api` instance to `context.configure()`. Additionally, ensure that your SQLAlchemy `Enum` columns in your models use `alembic_autogenerate_enums.ColumnEnum` for the library to track changes effectively. The `values_callable` argument is crucial for `ColumnEnum` to correctly extract enum values.

from alembic import context
from alembic_autogenerate_enums import create_api
from sqlalchemy import create_engine, MetaData

# Assuming these are defined elsewhere in your env.py
# target_metadata = Base.metadata or some other SQLAlchemy MetaData object
# config = context.config # Alembic configuration object

# Placeholder for database connection and target_metadata
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite:///:memory:')
connectable = create_engine(DATABASE_URL)
target_metadata = MetaData() # Replace with your actual target_metadata

# --- The key integration part in env.py ---

# 1. Create the API instance
api = create_api()

# 2. Integrate the API into context.configure
def run_migrations_online():
    with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=target_metadata,
            api=api, # Pass the API instance here!
            # include_object=include_object, # Optional, if you have one
            # process_revision_directives=process_revision_directives # Optional, if you have one
        )

        with context.begin_transaction():
            context.run_migrations()

# Call the function if running online
# if context.is_offline_mode():
#     run_migrations_offline()
# else:
#     run_migrations_online()

# Example model using ColumnEnum
import enum
from sqlalchemy.orm import declarative_base, Mapped, mapped_column
from sqlalchemy import String

Base = declarative_base()

class UserStatus(enum.Enum):
    ACTIVE = 'active'
    INACTIVE = 'inactive'
    PENDING = 'pending'

class User(Base):
    __tablename__ = 'users'
    id: Mapped[int] = mapped_column(primary_key=True)
    status: Mapped[UserStatus] = mapped_column(ColumnEnum(UserStatus, values_callable=lambda x: [m.value for m in x]))

print("Alembic Autogenerate Enums setup snippet ready for integration into env.py.")