Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alembic migrate with existing SQLAlchemy engine

I have a certain SQLAlchemy declarative Base that I create on a sqlite memory DB:

engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)

I'm using this for unit testing logic.

With this I have my tables in the DB. But now I wish to migrate certain things using alembic.

AFAIK alembic migrations use the env.py run_migrations_online and there uses a SQLAlchemy function called engine_from_config creating a new engine here.

The problem I wish to solve is to have a way to use the previously created connection, which holds the recently created tables, for the alembic migrations.

I used this on my test scripts: Using Alembic API from inside application code, so that my script does the following after the previous create_all call:

import alembic.config
alembicArgs = ['--raiseerr', '-x', 'dbPath=sqlite:///:memory:', 'upgrade', 'head']
alembic.config.main(argv=alembicArgs

[Please mind, I would just create my schemas with the Base.metadata.create_all(engine) call but my alembic versions not only hold schema changes, they also have some filling of catalog tables data, that's why I intend to use alembic here. In fact, if my alembic migrations hold some "create tables" logic, these two would conflict. So I can safely remove the create_all call and depend on alembic alone to create my schemas here.]

Having already modified my alembic's env.py:

def run_migrations_online():
    ini_section = config.get_section(config.config_ini_section)

    db_path = context.get_x_argument(as_dictionary=True).get('dbPath')

    if db_path:
        ini_section['sqlalchemy.url'] = db_path

    connectable = engine_from_config(
        ini_section,
        prefix ... # everything from here the same as default env.py

As far as I can tell the connectable=engine_from_config creates a connection to a new engine on a new sqlite:///:memory: database, and that's why I can't upgrade via alembic the previously created DB on my script with create_all(engine).

So... TLDR; is there a way to pass my previously existing engine connection (with my created tables) to alembic so that it can migrate it? (I'm pretty sure that the dbPath arg I created is useless here, in fact, I'm just copying what the other post I referenced uses).

like image 218
Javier Novoa C. Avatar asked Jan 19 '18 21:01

Javier Novoa C.


People also ask

How do you use alembic for migration?

Usage of Alembic starts with creation of the Migration Environment. This is a directory of scripts that is specific to a particular application. The migration environment is created just once, and is then maintained along with the application's source code itself.

What is SQLAlchemy migration?

The sqlalchemy migrations are one of the features, and it migrates to provide a way of dealing with the database schema and its changes in the sqlalchemy projects; it migrates the data with the help of some tools like alembic is one of the data migration tools that offer the Alter statement to the specific database to ...

How does alembic Autogenerate work?

The autogenerate process scans across all table objects within the database that is referred towards by the current database connection in use. The list of objects that are scanned in the target database connection include: The “default” schema currently referred towards by the database connection.

How do you change heads in alembic?

Show activity on this post. Delete (or move to another folder) the specific migration file (in migrations/versions folder). The head will automatically revert to the most recent remaining migration. Using stamp will set the db version value to the specified revision; not alter the head revision number.


1 Answers

You can create an alembic config instance and do operations on it:

def migrate_in_memory(migrations_path, alembic_ini_path=None, connection=None, revision="head"):
    config = alembic.config.Config(alembic_ini_path)
    config.set_main_option('script_location', migrations_path)
    config.set_main_option('sqlalchemy.url', 'sqlite:///:memory:')
    if connection is not None:
        config.attributes['connection'] = connection
    alembic.command.upgrade(config, revision)

It might take some fine tuning, but that's the general gist of things.

like image 184
Hetzroni Avatar answered Oct 10 '22 02:10

Hetzroni