Table of Contents
pip install edwh-migrate
# or to include extra dependencies (psycopg2, redis):
pip install edwh-migrate[full]These variables can be set in the current environment or via .env:
MIGRATE_URI(required): regularpostgres://user:password@host:port/databaseorsqlite:///path/to/databaseURIMIGRATIONS_FILE(optional) path to a file containing migrations. By default, a file calledmigrations.pyin the working directory is used.DATABASE_TO_RESTORE: path to a (compressed) SQL file to restore..xz,.gzand.sqlare supported.MIGRATE_CAT_COMMAND: for unsupported compression formats, this command decompresses the file and produces sql on the stdout.SCHEMA_VERSION: Used in case of schema versioning. Set by another process.REDIS_HOST: If set, all keys of the redis database 0 will be removed.MIGRATE_TABLE: name of the table where installed migrations are stored. Defaults toewh_implemented_features.FLAG_LOCATION: when using schema versioned lock files, this directory is used to store the flags. Defaults to/flags.CREATE_FLAG_LOCATION(bool): should the directory above be created if it does not exist yet? Defaults to 0 (false).SCHEMA: (for postgres) set the default namespace (search_path). Defaults topublic.USE_TYPEDAL: pass a TypeDAL instance to migrations instead of a regular pyDAL.
You can also set your config variables via the [tool.migrate] key in pyproject.toml.
First, these variables are loaded and then updated with variables from the environment.
This way, you can set static variables (the ones you want in git, e.g. the migrate_table name or path to the backup to
restore) in the toml, and keep private/dynamic vars in the environment (e.g. the database uri or schema version).
Example:
[tool.migrate]
migrate_uri = "" # filled in by .env
database-to-restore = "migrate/data/db_backup.sql"
# ...from edwh_migrate import migration
@migration
def feature_1(db):
print("feature_1")
return True
@migration(requires=[feature_1]) # optional `requires` ensures previous migration(s) are installed
def functionalname_date_sequencenr(db: pydal.DAL):
db.executesql("""
CREATE TABLE ...
""")
db.commit()
return TrueWhen your configuration is set up properly and you have a file containing your migrations, you can simply run:
migrate
# or, to use a different name than migrations.py:
migrate path/to/my/migrate_file.pyViewMigrationManager is designed to manage the lifecycle of view migrations in a database using context management. It ensures that migrations are properly handled with dependencies between different migrations.
-
Define Subclasses: Create subclasses of
ViewMigrationManagerand implement the required methodsupanddown.from edwh_migrate import ViewMigrationManager class MyExampleView_V1(ViewMigrationManager): # Define dependencies (optional) uses = () # Specify a migration that must have run before this class may be used since = "previous_migration" def up(self): # Logic to apply the migration self.db.executesql( ''' CREATE MATERIALIZED VIEW my_example_view AS SELECT id, name FROM my_table; ''' ) def down(self): # Logic to reverse the migration self.db.executesql( ''' DROP MATERIALIZED VIEW IF EXISTS my_example_view; ''' ) class AnotherExampleView(ViewMigrationManager): # This class depends on MyExampleView_V1 uses = (MyExampleView_V1,) def up(self): # Logic to apply the migration self.db.executesql( ''' CREATE MATERIALIZED VIEW another_example_view AS SELECT id, name FROM my_example_view; ''' ) def down(self): # Logic to reverse the migration self.db.executesql( ''' DROP MATERIALIZED VIEW IF EXISTS another_example_view; ''' )
-
Define the
previous_migration: Create a migration function that serves as the prerequisite forMyExampleView_V1.from edwh_migrate import migration @migration def previous_migration(db): db.executesql(''' CREATE TABLE my_table ( id SERIAL PRIMARY KEY, name VARCHAR(255) ); ''') db.commit() return True
-
Use the Subclass in a Migration Function: Utilize the subclass in a migration function to manage the view migration context.
from edwh_migrate import migration @migration def upgrade_some_source_table_that_my_example_view_depends_on(db): with MyExampleView_V1(db): db.executesql(''' ALTER TABLE my_table ADD COLUMN new_column VARCHAR(255); ''') db.commit() return True
In the example above:
MyExampleView_V1is a subclass ofViewMigrationManagerthat manages the lifecycle of a materialized view namedmy_example_view.AnotherExampleViewis another subclass that depends onMyExampleView_V1and manages the lifecycle of another materialized view namedanother_example_view.- The
upmethod inMyExampleView_V1contains the logic to create the materialized viewmy_example_view. - The
downmethod inMyExampleView_V1contains the logic to drop the materialized viewmy_example_view. - The
upmethod inAnotherExampleViewcontains the logic to create the materialized viewanother_example_viewthat referencesmy_example_view. - The
downmethod inAnotherExampleViewcontains the logic to drop the materialized viewanother_example_view. - The
sinceattribute specifies that a particular migration (previous_migration) must have run beforeMyExampleView_V1may be used. - The
previous_migrationfunction creates the tablemy_tableand serves as a prerequisite forMyExampleView_V1. - The
migrationdecorator is used to define a migration function (upgrade_some_source_table_that_my_example_view_depends_on) that executes within the context ofMyExampleView_V1. - The
with MyExampleView_V1(db)block ensures that thedownmethod is called before the block executes and theupmethod is called after the block completes.
In addition to 'since', the inverse 'until' can also be used.
edwh-migrate is distributed under the terms of the MIT license.