You can do this using the env.py
and/or the script.py.mako
files (see the UPDATE below).
Note : I always had only one row in my alembic_version
table. The following solution will be based on this asumption and will create an history table instead of updating the alembic_version
table. The mako file can be easily adapted in other cases.
- Create the history table
CREATE TABLE alembic_version_history (
version_num VARCHAR(32),
inserted_at TIMESTAMP,
message TEXT
)
- Update the
alembic/env.py
to prevent auto generated migrations to delete the newly created table
# add this function
def include_name(name, type_, parent_names):
if type_ == "table":
if name == "alembic_version_history":
return False
return True
[...]
# Use it in BOTH run_migrations_offline and run_migration_online functions as follows
context.configure([...], include_name=include_name)
- Update the code in the
script.py.mako
file, specifically the upgrade
function :
[...]
def upgrade():
${upgrades if upgrades else "pass"}
op.execute("INSERT INTO alembic_version_history (version_num, inserted_at, message) VALUES ('${up_revision}' ,NOW(), '${message}')")
Generate a migration alembic revision [--autogenerate] -m "test message"
. Then alembic upgrade head
to update alembic_version
table and add a new row to alembic_version_history
.
UPDATE :
Adding operations to the mako file means that anyone can edit/update/delete those.
To avoid this issue, we can again use the env.py
:
def update_history(ctx, step, heads, run_args):
revision_id = step.up_revision_id
if step.is_upgrade:
message = step.up_revision.doc
ctx.connection.execute(
f"INSERT INTO alembic_version_history (version_num, inserted_at, message) VALUES ('{revision_id}' ,NOW(), '{message}')"
)
print("INSERT NEW ROW IN HISTORY")
else:
ctx.connection.execute(f"DELETE FROM alembic_version_history where version_num = '{revision_id}'")
print("DELETE LAST ROW IN HISTORY")
configure.context([...], include_name=include_name, on_version_apply=update_history)