Skip to main content

Live Alembic

Apply database migrations without migration files. Live Alembic uses Alembic's autogeneration to diff your SQLAlchemy models against the live database and applies DDL directly — no versions/ directory, no commit-to-run cycle.

Install

pip install live-alembic

Quickstart

1. Create migrate_env.py in your project root:

from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base

# SQLite
db_url = "sqlite:///myapp.db"

# PostgreSQL
# db_url = "postgresql+psycopg2://user:password@localhost/mydb"

# MySQL / MariaDB
# db_url = "mysql+pymysql://user:password@localhost/mydb"

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id    = Column(Integer, primary_key=True)
    name  = Column(String(100))
    email = Column(String(255))

target_metadata = Base.metadata

# Optional: List of tables to ignore (e.g. tables managed by other applications)
# ignore_tables = ["some_external_table"]

2. Check for pending changes:

livealembic check

Exits 0 if the schema is in sync, 1 if changes are pending.

3. Apply changes:

livealembic migrate

CLI Reference

livealembic check   [--file PATH] [--format text|json] [--verbose]
livealembic migrate [--file PATH] [--format text|json] [--verbose]
livealembic --version
Command Description Exit code
check Show pending changes, do not apply 1 if changes exist
migrate Apply pending changes 1 on error
Option Default Description
--file PATH migrate_env.py Path to your configuration module
--format json text Machine-readable JSON output
--verbose off Include raw operation details in output

Example output

  Create table "orders"
  Add column 'phone' (VARCHAR(20)) to table 'users'
  Drop column 'legacy_id' from table 'users'
  Alter column 'status' on 'orders' to type INTEGER

CI / CD usage

# Fail the pipeline if the schema has drifted
livealembic check --file config/migrate_env.py

# Apply during deployment
livealembic migrate --file config/migrate_env.py

Package Entry Points

Other libraries can automatically include SQLAlchemy models into the application's migration metadata by registering an entry point under the live_alembic.models group. Live Alembic dynamically loads all registered entry points under this group and merges their tables into the application's target_metadata.

The entry point can point to:

  • A SQLAlchemy MetaData instance.
  • A SQLAlchemy Declarative Base (or registry) subclass with a metadata attribute.
  • A SQLAlchemy Table object.
  • A module—in which case Live Alembic will inspect its attributes to discover any MetaData, Declarative Base, or Table instances.

Example registration in a library's pyproject.toml:

[project.entry-points."live_alembic.models"]
my_library_models = "my_library.models:metadata"

Hooks

Define before_migrate, migrate_data and/or after_migrate in migrate_env.py to run data migrations in the same transaction as the schema change:

from live_alembic import Changes
from sqlalchemy import text

def before_migrate(changes: Changes, connection):
    if changes.added_column("users", "full_name"):
        connection.execute(
            text("UPDATE users SET full_name = first_name || ' ' || last_name")
        )

def after_migrate(changes: Changes, connection):
    if changes.dropped_column("users", "legacy_token"):
        connection.execute(text("DELETE FROM audit_log WHERE action = 'token_refresh'"))

A third hook, migrate_data, runs between the additive and the destructive operations. Defining it splits the migration into two phases -- creates and adds first, then the hook, then the drops and alters -- so the hook sees a schema where both the old and the new structures exist. That is what an installation which skipped a release needs, because it receives the new table and the dropped column in the same migration:

def migrate_data(changes: Changes, connection):
    if changes.created_table("user_emails") and changes.has_column("users", "email"):
        connection.execute(text(
            "INSERT INTO user_emails (user_id, address) "
            "SELECT id, email FROM users WHERE email IS NOT NULL"
        ))

changes.has_column() reflects the live database at the moment the hook runs, so it also keeps the hook from firing on a fresh install where the source column never existed. Note that alter_column runs in the second phase, which is what makes "add column, backfill, then set NOT NULL" work in a single pass.

All three hooks receive a Changes helper and the active connection. If a hook raises, the transaction is rolled back (schema + data).

See documentation/usage/usage.org for the full hooks API, database-specific notes, and programmatic API docs.

Testing

Complete development tests require docker compose for database integration.

Local Tests

Run the standard test suite against SQLite (no Docker required):

make test

Database Integration Tests

Start database services and run tests against a specific backend:

make test-postgresql
make test-mysql

Or run against all supported databases sequentially:

make test-all

Test Matrix (Tox)

Run the full matrix of supported Python versions and databases:

make tox

Coverage

Generate a coverage report for the SQLite tests:

make coverage

Metadata

Release files for live-alembic 0.3.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for live-alembic 0.3.6
File Size Uploaded
live_alembic-0.3.6.tar.gz 30.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for live-alembic 0.3.6
File Interpreter ABI Platform
live_alembic-0.3.6-py3-none-any.whl Python 3 none any Details

Total release size: 62.0 kB

Release files / live_alembic-0.3.6.tar.gz

Download URL live_alembic-0.3.6.tar.gz
Size 30.1 kB
Tags Source
SHA-256 checksum
How to use checksums
c577578695238779844a82a01a4fa33cbd1747b40d6e08f08abfa8043a2ec1b8
BLAKE2b-256 checksum
How to use checksums
6f0203365393285ce6b3ed31b5dc643443dcdd9f7f1ce5ed4e78124e0c8a0af9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / live_alembic-0.3.6-py3-none-any.whl

Download URL live_alembic-0.3.6-py3-none-any.whl
Size 32.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8f4f5061b4e9a947a5b90503b5526b786fc9252d10ad3327600bb117d910ebe8
BLAKE2b-256 checksum
How to use checksums
6763c44a76715cd3394fd287b7b86584a7968d2b9a2ea94014840102fac33a5c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.3.6 This release

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page