Alembic Autogenerate Enums
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.
Common errors
-
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.fixEnsure your `env.py` has `api = create_api()` and then `context.configure(..., api=api, ...)`. -
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`.fixVerify 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`. -
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.fixEnsure 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.
Warnings
- 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.
- 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.
- 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.
Install
-
pip install alembic-autogenerate-enums
Imports
- ColumnEnum
from alembic_autogenerate_enums import ColumnEnum
- create_api
from alembic_autogenerate_enums import create_api
Quickstart
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.")