SQLAltery
A migration library for SQLAlchemy. A cleaner frontend to Alembic.
Guide
Set up management script
SQLAltery provides the "functional core".
To use it in your project, you'll need to provide your own "imperative shell", but this is easy.
Here's a starter managedb.py:
import sqlalchemy as sa
from sqlaltery import SQLAltery
def cmd_diff() -> None:
# Show pending changes to schema
salt = _get_sqlaltery()
print(salt.diff(model.Base.metadata))
def cmd_commit(*, data: bool = False) -> None:
# Create a new migration file from pending changes (if any)
salt = _get_sqlaltery()
salt.generate(model.Base.metadata, data = data)
def cmd_sync(*, revision: int | None = None, fake: bool = False) -> None:
# Migrate your DB to `revision`. If `None`, this means the latest.
# `revision == 0` is a special revision that means an empty DB.
salt = _get_sqlaltery()
engine = _get_engine()
with salt.connect_to(engine) as migrator:
migrator.migrate(revision, fake = fake)
def _get_sqlaltery() -> SQLAltery:
# choose where to store migration files
from pathlib import Path
migration_dir = Path(__file__).parent / 'migration'
return SQLAltery(migration_dir)
def _get_engine() -> sa.Engine:
# an engine pointing to the DB you want to migrate
return sa.create_engine(sa.URL.create(...))
def _get_metadata() -> sa.MetaData:
# return your model metadata
from my_app import my_model
return my_model.Base.metadata
if __name__ == '__main__':
import funcli
funcli.run({ 'diff': cmd_diff, 'commit': cmd_commit, 'sync': cmd_sync })
Terminology used here
You can name the operations anything, but this guide will use these terms:
commit: create a new migration (Django:py manage.py makemigrations)sync: apply migrations (Django:py manage.py migrate)sync --fake: fake migrations (Django:py manage.py migrate --fake); this updates only the migration metadata (tablesqlaltery_migration)
Reset migrations
Assuming your database(s) are synced to latest, delete your migration dir, create a new migration, and do a fake sync:
rm -rf migration
py managedb.py commit
py managedb.py sync --fake
Data migrations
Data migrations usually involve a three-step process: expand, project, contract.
- Expand the schema:
T_oldtoT_exp \superset T_new, whereT_expis such that the underlying database can automatically project the data onto it (e.g., adding only nullable columns) - Project the data: apply a function
T_exp -> T_newto the data; schema stays the same - Contract the schema:
T_exptoT_new
SQLAltery can automatically split the migration into these steps. Run:
py managedb.py commit --data
Now, modify the newly created migration to write data migration code in the forwards and backwards
methods in the generated file. You can access the current sa.MetaData in self.md.
Once you're done, sync:
py managedb.py sync
Status
-
MetaDatadiffing (sqlaltery/compare.py) - apply ops to
MetaData(sqlaltery/ops.py:apply) - apply ops to
Connection(sqlaltery/ops.py:apply); all the heavy lifting is passed to Alembic's DDL - write migration to a file (
sqlaltery/command.py:_save_migration) - allow multiple migrations per file (
OPSorOPS_{\d+}) -
generate(data: bool)that creates a new migration with expand/project/contract phases - tests (95%)
- docs
- document basic usage and show example script
Why not use Alembic directly?
- Alembic diffs the head MD against current DB, not against latest migration
- Alembic has split upgrade/downgrade
- Alembic requires you to manually manage metadata in migrations (for doing queries)
- Alembic creates a bunch of files (alembic.ini, env.py). The default case should be simple: just create an
SQLAlteryand use it; customize by passing arguments.
Internal use
Set up:
uv sync --no-install-workspace
Run checks:
py -m ruff check sqlaltery tests
py -m mypy sqlaltery tests
py -m pytest
How to publish:
bash build.sh
py -m twine upload artifact/dist/<file>
Metadata
Release files for sqlaltery 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlaltery-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / sqlaltery-0.6.0-py3-none-any.whl
| Download URL | sqlaltery-0.6.0-py3-none-any.whl |
|---|---|
| Size | 16.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4b7e1cc61dbf2f7bebe89f5d6662268a6176743f7a4212788574e86621dfc8a6
|
|
BLAKE2b-256 checksum How to use checksums |
dde9257c15a1b85379de14a5c09644620be64e94298b52d956e63680fec2cc97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.3
|