Pogchamp-company / alembic-postgresql-enum

Alembic autogenerate support for creation, alteration and deletion of enums
MIT License
166 stars 9 forks source link

UUID object has no attribute replace error in sync_enum_values command #84

Open lotruheawea opened 5 days ago

lotruheawea commented 5 days ago

Hi I try to reduce my problem to the most relevant bits. My ORM looks like this:

class UnitORM(Base):
    __tablename__ = "unit"
    __table_args__ = (UniqueConstraint("identifier", "identifier_type"),)

    id = Column(BigInteger, primary_key=True, autoincrement=True)
    resource_id: Mapped[uuid.UUID] = Column(
        UUID(as_uuid=True), nullable=False, unique=True
    )
    identifier_type: Mapped[IdentifierType] = Column(
        Enum(IdentifierType, name="identifier_type_enum"), nullable=False
    )
    identifier: Mapped[int] = Column(BigInteger, nullable=False)

I have updated the IdentifierType Enum like so:

class IdentifierType(enum.StrEnum):
    A = "a"
    B = "b"
    # this is new
    C = "c"

and got these migrations:

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.sync_enum_values(
        "public",
        "identifier_type_enum",
        ["A", "B", "C"],
        [
            TableReference(
                table_schema="public", table_name="unit", column_name="identifier_type"
            )
        ],
        enum_values_to_rename=[],
    )
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.sync_enum_values(
        "public",
        "identifier_type_enum",
        ["A", "B"],
        [
            TableReference(
                table_schema="public", table_name="unit", column_name="identifier_type"
            )
        ],
        enum_values_to_rename=[],
    )
    # ### end Alembic commands ###

And I get this error in my pytest-alembic.tests.test_up_down_consistency:

...
E               pytest_alembic.plugin.error.AlembicTestFailure: Failed to upgrade through each revision individually after performing a roundtrip upgrade -> downgrade -> upgrade cycle.
E               
E               Failing Revision:
E                   XXX
E               
E               Alembic Error:
E                   (builtins.AttributeError) 'UUID' object has no attribute 'replace'
E                   [SQL: INSERT INTO unit (id, resource_id, identifier_type, identifier) VALUES (%(id)s, %(resource_id)s, %(identifier_type)s, %(identifier)s)]

How can this be solved? Thank you very much for your support!

RustyGuard commented 2 days ago

I have tried to reproduce your issue in a test case. It passes. Either you have a problem with your test or I am missing something that you have in it.

lotruheawea commented 2 days ago

Mmh, we use sqlalchemy v1.4.52 and alembic v.1.13.2. Maybe it is because we use the old version of sqlalchemy?

RustyGuard commented 1 day ago

Our tests run with different sqlalchemy versions. sync_enum_values does not use insert under the hood. I'm inclined to think that something is wrong in your test

lotruheawea commented 1 day ago

I tried to do some research, but also found this dead end: https://stackoverflow.com/questions/59082387/attributeerror-uuid-object-has-no-attribute-replace. I am not sure, if this is helpful. In the end, my test is the native pytest-alembic.tests.test_up_down_consistency (version v0.11.0)... I can also post the initial migration, if helpful:

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        "unit",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("resource_id", postgresql.UUID(as_uuid=True), nullable=False),
        sa.Column(
            "identifier_type",
            sa.Enum(
                "A",
                "B",
                name="identifier_type_enum",
            ),
            nullable=False,
        ),
        sa.Column("identifier", sa.BigInteger(), nullable=False),
        sa.Column(
            "created",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_unit")),
        sa.UniqueConstraint(
            "identifier", "identifier_type", name=op.f("uq_unit_identifier")
        ),
        sa.UniqueConstraint("resource_id", name=op.f("uq_unit_resource_id")),
    )
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table("unit")
    # ### end Alembic commands ###

    # Manually added commands to drop enums
    sa.Enum(name="identifier_type_enum").drop(op.get_bind(), checkfirst=False)