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

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.4
File Size Uploaded
live_alembic-0.3.4.tar.gz 18.6 kB Details

Built distribution (wheel)

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

Total release size: 35.3 kB

Release files / live_alembic-0.3.4.tar.gz

Download URL live_alembic-0.3.4.tar.gz
Size 18.6 kB
Tags Source
SHA-256 checksum
How to use checksums
b651427d158b2773222ee4638ec5765d40b01b30a3fb41d81b6121e86e99f423
BLAKE2b-256 checksum
How to use checksums
91010d1b85156552a07c2ea7bf356cc5b2d425e10896866ee66a2dcee7acf8e1
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.4-py3-none-any.whl

Download URL live_alembic-0.3.4-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d616f70976ebe6ab492f52a65127cad2ddc4ad23365400d891ca23d9a30ed45e
BLAKE2b-256 checksum
How to use checksums
baf984d5a0ce254480065f4c9b7eb94f7c997ae81ce7f2fa85c01dd7f022a6eb
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

0.3.5

2 release files

This release

0.3.4 This release

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