Alembic Utils Extended
Autogenerate Support for PostgreSQL Functions, Views, Materialized Views, Triggers, Policies, and Check Constraints
This is a fork of the much more popular alembic_utils package to extend the capabilities of Alembic, which adds support for autogenerating a larger number of PostgreSQL entity types, including functions, views, materialized views, triggers, and policies.
This repo adds additional support for defining indices for materialized views and autogenerating check constraints.
Quickstart
Visit the quickstart guide for usage instructions.
Entity Registration
# migrations/env.py
from alembic_utils_extended.pg_view import PGView
from alembic_utils_extended.replaceable_entity import register_entities
view = PGView(schema="public", signature="view", definition="SELECT 1")
register_entities([view])
Monitor Check Constraints
Check constraints defined in SQLAlchemy models can also be autogenerated. Note that check constraints must be named. Add
to your env.py:
# migrations/env.py
from alembic import context
context.configure(
# ... other configurations ...
compare_check_constraints=True,
)
Monitor Indexes
Alembic's built-in autogenerate on SQLAlchemy 1.4 mishandles several PostgreSQL index shapes — function expressions
(func.lower(col)), directional modifiers (desc(col), literal_column("col DESC")), postgresql_ops opclass hacks
for direction, and mixed shapes routinely produce wrong / duplicated diffs.
With compare_indexes=True, alembic_utils_extended takes over autogen for all user-declared indexes, reading the
DB side directly from pg_index and applying an identity-based diff. Consumers must also register an include_object
filter in env.py returning False for type_ == "index" so stock Alembic's index dispatcher doesn't fire and duel
with the fork. All indexes must be named.
# migrations/env.py
from alembic import context
def include_object(obj, name, type_, reflected, compare_to):
# alembic-utils-extended's `compare_indexes` comparator owns all index autogeneration.
# Skip stock Alembic's index dispatcher entirely to avoid dueling autogeneration.
if type_ == "index":
return False
return True
context.configure(
# ... other configurations ...
include_object=include_object,
compare_indexes=True,
)
Indexes backing PRIMARY KEY and UNIQUE constraints are excluded automatically (managed by stock Alembic's constraint diff).
Content changes under a stable name are not detected. Comparison is identity-only ((table_name, index_name) set
diff). If the same index name exists in both the model and the database, the comparator treats it as unchanged. To
evolve an index's columns, WHERE clause, opclass, INCLUDE list, or method, rename it (which produces a drop + create
pair the fork will emit) or write a manual migration. This is a real trade-off vs. stock Alembic, which detects column-
list changes for plain-column indexes — but stock Alembic's index handling has enough other bugs on SA 1.4 that
identity-only-plus-rename is easier to reason about than any partial coverage.
Coverage is best-effort, not guaranteed. Indexes can drift out of prod (manual CREATE INDEX, out-of-band drops)
in ways autogen against a local DB can never catch. This library closes the most common autogen bugs but does not
guarantee every declared index actually exists in your database. Audit periodically with a direct pg_index query —
see the auditing recipe below.
Common pitfalls the comparator catches at autogenerate time:
func.X("col_name")anti-pattern — bare strings insidefunc.X(...)are treated as bound-parameter literal values, not column references. The resulting index is on the constant string, not the column. Usefunc.X(table.c.col_name)orfunc.X(literal_column("col_name"))instead. The comparator raises with a remediation hint when it detects this.
Auditing indexes against production
Run against a prod replica to catch drift the fork can't detect on its own (indexes declared in code but missing from prod, or vice versa):
-- Lists every user-declared index in prod (excludes PK/UNIQUE constraint indexes).
-- Cross-reference against your model's declared index set.
SELECT
n.nspname AS schema_name,
t.relname AS table_name,
c.relname AS index_name,
pg_get_indexdef(i.indexrelid) AS index_definition
FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
JOIN pg_class t ON t.oid = i.indrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_constraint con ON con.conindid = i.indexrelid
WHERE n.nspname = 'public'
AND con.oid IS NULL
AND NOT i.indisprimary
ORDER BY t.relname, c.relname;
Autogeneration
The next time you autogenerate a revision, Alembic will detect if your entities are new, updated, or removed and populate the migration script.
alembic revision --autogenerate -m 'message'
Contributing
If you have any issues with contributing, please reach out to justin@joincandidhealth.com so that we can work out any issues you are having! This is mostly just forked directly from alembic_utils, so it's possible something is misconfigured.
Testing
poetry install
poetry run pre-commit run --all-files
poetry run pytest
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file alembic_utils_extended-1.2.1.tar.gz.
File metadata
- Download URL: alembic_utils_extended-1.2.1.tar.gz
- Upload date:
- Size: 30.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c87f1edf9fcb7fcd0750daa85e549a72fed4f97c2c4e58685f012a72fcf8289
|
|
| MD5 |
6befe9a8d635e8137f0796a2dbe75951
|
|
| BLAKE2b-256 |
a69e67cfdec65d48e25aa30278446d6edc55417fbf0c5b31728bc2e88b20c063
|
Provenance
The following attestation bundles were made for alembic_utils_extended-1.2.1.tar.gz:
Publisher:
publish.yml on candidhealth/alembic-utils-extended
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
alembic_utils_extended-1.2.1.tar.gz -
Subject digest:
3c87f1edf9fcb7fcd0750daa85e549a72fed4f97c2c4e58685f012a72fcf8289 - Sigstore transparency entry: 2131965060
- Sigstore integration time:
-
Permalink:
candidhealth/alembic-utils-extended@0cc2ff433e58e29e613cd26832df1093ebc3a5c8 -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/candidhealth
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0cc2ff433e58e29e613cd26832df1093ebc3a5c8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file alembic_utils_extended-1.2.1-py3-none-any.whl.
File metadata
- Download URL: alembic_utils_extended-1.2.1-py3-none-any.whl
- Upload date:
- Size: 40.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4627b5049a0cd66f1bd62cbde2dbca80b3d5dc499838a74c17ea04fc28d3c2d2
|
|
| MD5 |
8230edfbf9717f95bc1865dbdeac8782
|
|
| BLAKE2b-256 |
225ce231fc51294f59fbe3e2de776010bbad7113c9b1e2f2a23fe0192ee32695
|
Provenance
The following attestation bundles were made for alembic_utils_extended-1.2.1-py3-none-any.whl:
Publisher:
publish.yml on candidhealth/alembic-utils-extended
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
alembic_utils_extended-1.2.1-py3-none-any.whl -
Subject digest:
4627b5049a0cd66f1bd62cbde2dbca80b3d5dc499838a74c17ea04fc28d3c2d2 - Sigstore transparency entry: 2131965477
- Sigstore integration time:
-
Permalink:
candidhealth/alembic-utils-extended@0cc2ff433e58e29e613cd26832df1093ebc3a5c8 -
Branch / Tag:
refs/tags/v1.2.1 - Owner: https://github.com/candidhealth
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0cc2ff433e58e29e613cd26832df1093ebc3a5c8 -
Trigger Event:
release
-
Statement type: