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.5

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.5
File Size Uploaded
live_alembic-0.3.5.tar.gz 28.6 kB Details

Built distribution (wheel)

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

Total release size: 56.4 kB

Release files / live_alembic-0.3.5.tar.gz

Download URL live_alembic-0.3.5.tar.gz
Size 28.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c23219a53c20d9be7be0f613d6da559485c15507efd8dd6e231bdf794018c9d2
BLAKE2b-256 checksum
How to use checksums
b6df3e60d5f4b1959703d01fe493dcdbfa521ae84e97265515b65640ac995439
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.5-py3-none-any.whl

Download URL live_alembic-0.3.5-py3-none-any.whl
Size 27.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cd3586229a7ddb96eb5ff241feda20dc72580c22ebddfdef9824107ba1d73627
BLAKE2b-256 checksum
How to use checksums
41ddf521bc4a3122d6189e625c9590110c5d311fff8acdf4f347b286e9f0b99c
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

0.3.6

2 release files

This release

0.3.5 This release

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