Migrations concepts#
RESTAlchemy uses explicit migration files to evolve the database schema.
Migrations live in a directory (e.g. examples/migrations/) and are applied using CLI tools such as ra-new-migration, ra-apply-migration, ra-rollback-migration and ra-rename-migrations.
Migration files#
Each migration is a Python file with a migration_step object:
from restalchemy.storage.sql import migrations
class MigrationStep(migrations.AbstractMigrationStep):
def __init__(self):
self._depends = ["<prev-migration-file>.py"]
@property
def migration_id(self):
return "<uuid>"
@property
def is_manual(self):
return False
def upgrade(self, session):
# Apply changes
pass
def downgrade(self, session):
# Revert changes
pass
migration_step = MigrationStep()
The template for new migrations is located at:
restalchemy/storage/sql/migration_templ.tmpl
and is used by ra-new-migration.
Migration identifiers and dependencies#
Each migration has:
- A UUID (
migration_idproperty). - A list of dependencies (
self._depends), which is a list of migration filenames. - A manual flag (
is_manualproperty).
Dependencies form a directed acyclic graph. When applying a migration, its dependencies are applied first.
Special constants:
HEAD— special name meaning "the latest automatic migration".MANUAL— marker used in migration numbers for manual migrations.
The MigrationEngine uses these rules to:
- Resolve partial names to filenames.
- Compute the head migration.
- Ensure that automatic migrations do not depend on manual ones (for
ra-new-migrationwithout--manual).
Migration filenames and numbering#
New migrations follow the new naming scheme:
migration_number— zero-padded number, e.g.0001,0002, ...message-with-dashes— taken from--message, spaces replaced with-.hash— first 6 characters of the migration UUID.
For manual migrations the number is MANUAL:
The old naming scheme is still supported; the ra-rename-migrations tool can convert old names to the new scheme.
If your message starts with a 4-digit number equal in length to the default migration number, that number is used directly as migration_number.
Migration state tracking#
Applied migrations are tracked in a dedicated table:
- Table name:
ra_migrations. - Model:
MigrationModel(restalchemy.storage.sql.migrations.MigrationModel). - Fields:
uuid(primary key) — migration UUID.applied(boolean) — whether the migration is applied.
The MigrationEngine ensures that this table exists and uses it to decide:
- Which migrations still need to be applied.
- Whether a migration can be rolled back.
Automatic vs manual migrations#
- Automatic migrations (
is_manual == False): - Can depend only on other automatic migrations.
- Are considered when computing the head migration.
- Manual migrations (
is_manual == True): - Often contain non-reversible or environment-specific changes.
- Are skipped when searching for the head migration.
The ra-new-migration command validates that automatic migrations do not depend on manual ones. If such a dependency is found, the command exits with an error.
High-level workflow#
At a high level, migrations are used as follows:
- Define DM models and update them over time.
- When the schema changes, create a new migration file with
ra-new-migration. - Implement
upgrade()/downgrade()inside the migration file. - Apply migrations to a specific database with
ra-apply-migration. - If necessary, roll back migrations with
ra-rollback-migration. - When adopting the new naming scheme, use
ra-rename-migrationsto rename existing migration files and update their dependencies.