1

I'm trying to create automatic tests for alembic, specifically to test that the revision --autogenerate command provides the expected migrations. Instead of running from the command line, I want to obtain the suggested revisions programmatically and run them to confirm that the effect on the test database is as expected.

I have two models - initial_model_base and removed_column_base, the idea is that when I run an autogenerated revision for the second time, alembic should detect that the second model has 1 less column and create a revision to remove it. However, I'm getting ValueError: no dispatch function for object: <alembic.operations.ops.ModifyTableOps. Here is the full stack trace:

File "C:\Users\Ronel\Desktop\repos\test_alembic\tests\tests.py", line 152, in test_removing_column() File "C:\Users\Ronel\Desktop\repos\test_alembic\tests\tests.py", line 109, in test_removing_column ops.invoke(op) File "C:\Users\Ronel\AppData\Roaming\Python\Python310\site-packages\alembic\operations\base.py", line 396, in invoke fn = self._to_impl.dispatch( File "C:\Users\Ronel\AppData\Roaming\Python\Python310\site-packages\alembic\util\langhelpers.py", line 258, in dispatch raise ValueError("no dispatch function for object: %s" % obj) ValueError: no dispatch function for object: <alembic.operations.ops.ModifyTableOps object at 0x0000028C0FBC1ED0>

The table I use for testing is test_alembic_schema.employees so to prevent other tables getting affected I provided an include_object function to the config kwargs.

def include_object(object, name, type_, reflected, compare_to):

    if type_ == "table":
        if object.schema == "test_alembic_schema" and object.name == 'employees':
            return True
        else:
            return False
    else:
        return True


mc = MigrationContext.configure(
    connection=DB_DEV.connection,
    opts={
        'compare_type': True,
        'include_schemas': True,
        'include_object': include_object
    }
)

ops = Operations(mc)


def test_creation():
    diff_ops = produce_migrations(
        context=mc,
        metadata=initial_model_base.metadata
    )

    with DB_DEV.transaction(autocommit=True):
        for op in diff_ops.upgrade_ops.ops:
            ops.invoke(op)


    employees = DB_DEV.get_table(table_name='employees', schema_name='test_alembic_schema')
    assert len(employees.c) == 8

    assert employees.c.id.primary_key
    assert not employees.c.first_name.primary_key



def test_removing_column():

    diff_ops = produce_migrations(
        context=mc,
        metadata=removed_column_base.metadata
    )

    with DB_DEV.transaction(autocommit=True):
        for op in diff_ops.upgrade_ops.ops:
            ops.invoke(op)

    employees = DB_DEV.get_table(table_name='employees', schema_name='test_alembic_schema')

    col_found = False
    for col in employees.columns:
        if col.name == 'created':
            col_found = True

    if col_found:
        assert True
    else:
        assert False

I am confused as to why the operation to create a table succeeds but to amend it fails. Do you have ideas how this can be fixed so both operations work as expected?

  • Answer: https://stackoverflow.com/questions/74892218/run-alembic-operations-manually-by-code-using-an-sqlalchemy-engine/74892219#74892219 – Denny Weinberg Dec 22 '22 at 18:38

0 Answers0