Skip to main content

alembic-pg-autogen

PyPI Python License: MIT CI Coverage Docs Downloads

Status: Beta — the core pipeline is stable and tested against real PostgreSQL. The API may still evolve before 1.0, but the library is suitable for production use.

Alembic autogenerate extension for PostgreSQL functions and triggers. Declare your DDL strings and let alembic revision --autogenerate figure out the CREATE, DROP, and CREATE OR REPLACE for you.

alembic-pg-autogen logo

Background

alembic_utils pioneered autogenerate support for PostgreSQL objects and has been hugely helpful to the community. This project takes a different approach aimed at faster performance on large schemas with many functions and triggers.

How it works

You declare your desired functions and triggers as plain DDL strings. At autogenerate time, the extension inspects the live database catalog, canonicalizes your DDL via a temporary savepoint, diffs current vs. desired state, and emits migration ops in dependency-safe order.

Quick example

import alembic_pg_autogen  # noqa: F401  # registers the comparator plugin

PG_FUNCTIONS = [
    """
    CREATE OR REPLACE FUNCTION set_updated_at()
    RETURNS trigger LANGUAGE plpgsql AS $$
    BEGIN
        NEW.updated_at = now();
        RETURN NEW;
    END;
    $$
    """,
]

PG_TRIGGERS = [
    """
    CREATE TRIGGER set_updated_at_on_update
    BEFORE UPDATE ON my_table
    FOR EACH ROW EXECUTE FUNCTION set_updated_at()
    """,
]

# in run_migrations_online():
context.configure(
    connection=connection,
    target_metadata=target_metadata,
    pg_functions=PG_FUNCTIONS,
    pg_triggers=PG_TRIGGERS,
)
alembic revision --autogenerate -m "add audit function and trigger"

What gets managed

An object type becomes managed when you declare it. Within a managed type the declared set is the whole truth, so objects found in the inspected schemas that you did not declare are dropped — that is what makes the tool declarative.

A key you never pass leaves that object type alone entirely: nothing is inspected, diffed, or emitted for it. The example above declares functions and triggers, so views are untouched, and adopting the library one object type at a time is safe.

To record the opt-out at the configuration site — or to set it conditionally — pass the IGNORED sentinel, which means exactly what omitting the key means:

from alembic_pg_autogen import IGNORED

context.configure(
    connection=connection,
    target_metadata=target_metadata,
    pg_functions=PG_FUNCTIONS,
    pg_triggers=PG_TRIGGERS,
    pg_views=IGNORED,  # not ready to manage views yet — don't drop them
)

An empty list is a different thing again: pg_views=[] declares "there should be no views" and drops every existing one, while pg_views=IGNORED declares "views are not managed here". Watch for this if you build the list dynamically — collect_view_ddl() or IGNORED keeps an empty result from clearing your schema.

Because an unrecognized key leaves its type unmanaged, a misspelled one (pg_view for pg_views) would quietly manage nothing; those are reported as a warning naming the key you meant.

Check constraints

Alembic detects when a named CHECK constraint is added to or removed from your models, but two constraints that share a name are always presumed equivalent — normalizing SQL expressions across backends is not something Alembic can do. So tightening amount >= 0 to amount > 0 in a model generates nothing, and the schema drifts.

This package closes that gap for PostgreSQL. Keep declaring constraints in SQLAlchemy metadata as usual:

class Order(Base):
    __tablename__ = "orders"

    id: Mapped[int] = mapped_column(primary_key=True)
    amount: Mapped[Decimal]

    __table_args__ = (CheckConstraint("amount > 0", name="ck_orders_amount"),)

and a changed expression now produces a migration:

def upgrade() -> None:
    op.drop_constraint("ck_orders_amount", "orders", type_="check")
    op.create_check_constraint("ck_orders_amount", "orders", "amount > 0")

There is nothing to configure. Each expression is round-tripped through PostgreSQL — added to the table as a throwaway NOT VALID constraint inside a savepoint that is rolled back — so amount >= 0 and the catalog's amount >= 0::numeric are recognized as the same constraint, and a real change is recognized as a real change.

Installation

pip install alembic-pg-autogen

Requires Python 3.10+ and SQLAlchemy 2.x. Bring your own PostgreSQL driver (psycopg, psycopg2, asyncpg, etc.). This package depends on postgast for DDL parsing, which requires protobuf >= 5.27.

Documentation

Full documentation is available at alembic-pg-autogen.readthedocs.io, including a quick-start guide, migration instructions for alembic_utils users, and API reference.

Development

make install     # Install dependencies (uses uv)
make lint        # Format (mdformat, codespell, ruff) then type-check (basedpyright)
make test        # Run full test suite (requires Docker for integration tests)
make test-unit   # Run unit tests only (no Docker needed)

License

MIT

Release files for alembic-pg-autogen 0.2.0

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

Source distribution (sdist)

Source distribution for alembic-pg-autogen 0.2.0
File Size Uploaded
alembic_pg_autogen-0.2.0.tar.gz 1.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for alembic-pg-autogen 0.2.0
File Interpreter ABI Platform
alembic_pg_autogen-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / alembic_pg_autogen-0.2.0.tar.gz

Download URL alembic_pg_autogen-0.2.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
21c112a572001c881cbf79e9d069cccde8702fec5763acb5377ad556678b6696
BLAKE2b-256 checksum
How to use checksums
cb87d709a860ab39eaecabec88078cce32b64a2c50e3e91338e9ed660c58e315
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / alembic_pg_autogen-0.2.0-py3-none-any.whl

Download URL alembic_pg_autogen-0.2.0-py3-none-any.whl
Size 24.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
501b879a6ce462592a8ef26e26dd85587275960725c01a79c74a3d03116fc65f
BLAKE2b-256 checksum
How to use checksums
fe9084d406f9f2bcc52765338e98700a11faa1dd34e1de7bbdd7240119df8de5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","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.0

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

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