upgrade database programmatically without template #807
Replies: 3 comments 6 replies
-
try looking at the example here: https://alembic.sqlalchemy.org/en/latest/cookbook.html#run-alembic-operation-objects-directly-as-in-from-autogenerate but alembic is not really intended to automatically upgrade a database without manual intervention. you'd want to look at Django's migrations for that kind of thing. |
Beta Was this translation helpful? Give feedback.
-
According to your comment I continue here: I think I missexplained my project: the goal of my framework is allow to add modules (from github as example) to a directory, then it will load the modules app.py: def get_app():
app = FastAPI()
for path in Path("").glob("modules/*"):
if path.is_dir():
module_name = ".".join(path.parts[:-1] + (path.stem, "main"))
logger.info(f"loading module {module_name}")
try:
module = import_module(module_name)
try:
getattr(module, "load_module")(app)
except AttributeError:
logger.error(f"Could not load module {module_name} ! missing entrypoint !")
# there is no main.py in the module
except ModuleNotFoundError:
logger.error(f"Could not load module {module_name} missing main.py!")
db.init_app(app)
return app the structure of a modular app should look like:
and I have a cli which init the project: # init <project_name>
@cli.command(name="init")
@click.argument("project_path", type=click.Path())
def cli_projet_init(project_path):
"""
Init a new Modular project
"""
p = Path(project_path)
# check if the path already exists
if p.is_dir():
click.secho(f"{error_style} the path `{p}` already exist !")
exit(1)
# Initialization
click.echo(f"{info_style} Initializing a new projet at `{p}` ...")
(p / "modules").mkdir(parents=True)
try:
with open(os.devnull, "w") as f:
with redirect_stdout(f):
command.init(
config=alembic_cfg,
directory="db_migrations",
template="default",
package=True,
)
except UnboundLocalError: # temporary du to a bug in alembic (issue 808)
pass
# creating the venv
click.echo(f"{info_style} Creating the venv ...")
venv.EnvBuilder(with_pip=True).create(p / "venv")
# install dependancies
click.echo(f"{info_style} Installing dependancies in the venv ...")
# on Windows
if os.name == "nt":
python_path = p / "venv" / "Scripts" / "python.exe"
# on Unix
else:
python_path = p / "venv" / "bin" / "python"
subprocess.run([python_path, "-m", "pip", "install", "-U", "pip"], check=True)
subprocess.run(
[
python_path,
"-m",
"pip",
"install",
"-r",
Path(__file__).parent / "venv_requirements.txt",
],
check=True,
)
shutil.copyfile(
src=Path(__file__).parent / "venv_requirements.txt",
dst=p / "requirements.txt",
) as you can see I don't use ini file, should I ? |
Beta Was this translation helpful? Give feedback.
-
This is what I'm doing to programatically run the migrations from the Any feedback is welcome. def apply_db_migrations(db_conn: Connection) -> None:
"""Apply alembic database migrations.
This method will first check if the database is empty (no applied alembic revisions),
in which case, it use SQLAlchemy to create all tables and then stamp the database for alembic.
If the database is not empty, it will apply all necessary migrations, bringing the database
up to date with the latest revision.
"""
# Create a standalone minimal config, that doesn't use alembic.ini
# (we don't want to load env.py, since they do a lot of things we don't want
# like setting up logging in a different way, ...)
alembic_cfg = alembic.config.Config()
alembic_cfg.set_main_option("script_location", "alembic-migrations")
alembic_cfg.set_main_option("sqlalchemy.url", SQLALCHEMY_URL)
script = ScriptDirectory.from_config(alembic_cfg)
def retrieve_migrations(rev: str, context: MigrationContext) -> list[RevisionStep]:
"""Retrieve all remaining migrations to be applied to get to "head".
The returned migrations will be the migrations that will get applied when upgrading.
"""
migrations = script._upgrade_revs("head", rev) # pyright: ignore[reportPrivateUsage]
if len(migrations) > 0:
log.info(f"Applying {len(migrations)} database migrations")
else:
log.debug("No database migrations to apply, database is up to date")
return migrations
env_context = EnvironmentContext(alembic_cfg, script)
env_context.configure(connection=db_conn, target_metadata=Base.metadata, fn=retrieve_migrations)
context = env_context.get_context()
current_rev = context.get_current_revision()
# If there is no current revision, this is a brand new database
# instead of going through the migrations, we can instead use metadata.create_all
# to create all tables and then stamp the database with the head revision.
if current_rev is None:
log.info("Performing initial database setup (creating tables)")
Base.metadata.create_all(db_conn)
context.stamp(script, "head")
return
log.debug("Checking for database migrations")
with Operations.context(context) as _op, context.begin_transaction():
context.run_migrations() |
Beta Was this translation helpful? Give feedback.
-
Hi,
I'm creating a framework based on FastAPI and Gino and I need to create a cli command (I'm using click) which upgrade on the fly the database.
I want avoid the use of the classic template ( env.py / ini file /ect ... )
for now I have
But I don't know what to do with
alembic.operations.ops.MigrationScript
I'm not sure to be in the right way :(
Have a nice day!
edit:
I trying a different way, I'm inspiring by https://github.com/patiprecios/fastapi-migrations and https://github.com/miguelgrinberg/Flask-Migrate
the tree:
and the script:
I don't understand what is the issue :/
Beta Was this translation helpful? Give feedback.
All reactions