Search code examples
pythonpytestalembic

Override values in alembic env.py file


I am using alembic for database revisions pretty much as intended probably. Instead of defining the database string in alembic.ini, I am using the env.py file to dynamically get the database credentials from the config module, pretty much as follows:

SQLALCHEMY_DATABASE_URL = "%s://%s:%s@%s:%d/%s" % (settings.db_type, settings.db_username, settings.db_password, settings.db_host, settings.db_port, settings.db_database)

def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    connectable = create_engine(SQLALCHEMY_DATABASE_URL)

    with connectable.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )

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

The problem now is that I am adding pytest tests, which are using a separate database connection. Essentially what I am doing is creating a database, applying migrations, add test data, run tests, delete the database.

From within pytest I am using it as follows:

@pytest.fixture(scope="session")
def initialize_database(create_database):
    
    """
    Executes all alembic versions against the empty database to get the general structure up and running.

    No need to yield / delete as the entire database is deleted anyway as part of the `create_database` yield fixture
    """
    # retrieves the directory that *this* file is in
    root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
    # this assumes the alembic.ini is also contained in this same directory
    config_file = os.path.join(root_dir, "alembic.ini")
    migrations_dir = os.path.join(root_dir, "alembic")

    config = Config(file_=config_file)
    config.set_main_option("script_location", migrations_dir)
    command.upgrade(config, 'head')

I tried to specify different database credentials here, but the values in the env.py overrides it. How can I specify different database credentials in such a setup?


Solution

  • I found a solution myself, its actually quite simple. When calling the upgrade command from the test fixture, I set two additional config values as follows:

    config.set_main_option("is_testing", "True")
    config.set_main_option('sqlalchemy.url', SQLALCHEMY_DATABASE_URL)
    

    Within the env.py file, I can use this testing option to dynamically change the SQLAlchemy url as follows:

    SQLALCHEMY_DATABASE_URL = "%s://%s:%s@%s:%d/%s" % (settings.db_type, settings.db_username, settings.db_password, settings.db_host, settings.db_port, settings.db_database)
    
    if config.get_main_option("is_testing", "False") == "True":
        url = config.get_main_option("sqlalchemy.url")
    else:
        url = SQLALCHEMY_DATABASE_URL
    

    It works well like that.