Skip to content

Commit 4586c19

Browse files
authored
fix(alembic): migration generation produces duplicated unique constraints (#434)
Removes column re-ordering component from the Alembic migration template. It was incorrectly causing incorrect duplicate constraints to be generated. Fixes #427
1 parent 986b7d5 commit 4586c19

2 files changed

Lines changed: 3 additions & 78 deletions

File tree

advanced_alchemy/alembic/templates/asyncio/env.py

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import asyncio
22
from typing import TYPE_CHECKING, cast
33

4-
from sqlalchemy import Column, pool
4+
from sqlalchemy import pool
55
from sqlalchemy.ext.asyncio import AsyncEngine, async_engine_from_config
66

77
from advanced_alchemy.base import metadata_registry
88
from alembic import context
99
from alembic.autogenerate import rewriter
10-
from alembic.operations import ops
1110

1211
if TYPE_CHECKING:
1312
from sqlalchemy.engine import Connection
1413

1514
from advanced_alchemy.alembic.commands import AlembicCommandConfig
16-
from alembic.runtime.environment import EnvironmentContext
1715

1816
__all__ = ("do_run_migrations", "run_migrations_offline", "run_migrations_online")
1917

@@ -24,41 +22,6 @@
2422
writer = rewriter.Rewriter()
2523

2624

27-
@writer.rewrites(ops.CreateTableOp)
28-
def order_columns(
29-
context: "EnvironmentContext", # noqa: ARG001
30-
revision: tuple[str, ...], # noqa: ARG001
31-
op: ops.CreateTableOp,
32-
) -> ops.CreateTableOp:
33-
"""Orders ID first and the audit columns at the end.
34-
35-
Args:
36-
context: The context of the environment.
37-
revision: The revision of the environment.
38-
op: The operation to create the table.
39-
40-
Returns:
41-
The operation to create the table.
42-
"""
43-
special_names = {"id": -100, "sa_orm_sentinel": 3001, "created_at": 3002, "updated_at": 3003}
44-
cols_by_key = [ # pyright: ignore[reportUnknownVariableType]
45-
(
46-
special_names.get(col.key, index) if isinstance(col, Column) else 2000,
47-
col.copy(), # type: ignore[attr-defined]
48-
)
49-
for index, col in enumerate(op.columns)
50-
]
51-
columns = [col for _, col in sorted(cols_by_key, key=lambda entry: entry[0])] # pyright: ignore[reportUnknownVariableType,reportUnknownArgumentType,reportUnknownLambdaType]
52-
return ops.CreateTableOp(
53-
op.table_name,
54-
columns, # pyright: ignore[reportUnknownArgumentType]
55-
schema=op.schema,
56-
# TODO: Remove when https://github.com/sqlalchemy/alembic/issues/1193 is fixed # noqa: FIX002
57-
_namespace_metadata=op._namespace_metadata, # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
58-
**op.kw,
59-
)
60-
61-
6225
def run_migrations_offline() -> None:
6326
"""Run migrations in 'offline' mode.
6427

advanced_alchemy/alembic/templates/sync/env.py

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
import operator
21
from typing import TYPE_CHECKING, cast
32

4-
from sqlalchemy import Column, Engine, engine_from_config, pool
3+
from sqlalchemy import Engine, engine_from_config, pool
54

65
from advanced_alchemy.base import metadata_registry
76
from alembic import context
87
from alembic.autogenerate import rewriter
9-
from alembic.operations import ops
108

119
if TYPE_CHECKING:
1210
from sqlalchemy.engine import Connection
1311

1412
from advanced_alchemy.alembic.commands import AlembicCommandConfig
15-
from alembic.runtime.environment import EnvironmentContext
1613

17-
__all__ = ["do_run_migrations", "run_migrations_offline", "run_migrations_online"]
14+
__all__ = ("do_run_migrations", "run_migrations_offline", "run_migrations_online")
1815

1916

2017
# this is the Alembic Config object, which provides
@@ -23,41 +20,6 @@
2320
writer = rewriter.Rewriter()
2421

2522

26-
@writer.rewrites(ops.CreateTableOp)
27-
def order_columns(
28-
context: "EnvironmentContext", # noqa: ARG001
29-
revision: tuple[str, ...], # noqa: ARG001
30-
op: ops.CreateTableOp,
31-
) -> ops.CreateTableOp:
32-
"""Orders ID first and the audit columns at the end.
33-
34-
Args:
35-
context: The context of the environment.
36-
revision: The revision of the environment.
37-
op: The operation to create the table.
38-
39-
Returns:
40-
The operation to create the table.
41-
"""
42-
special_names = {"id": -100, "sa_orm_sentinel": 3001, "created_at": 3002, "updated_at": 3003}
43-
cols_by_key = [ # pyright: ignore[reportUnknownVariableType]
44-
(
45-
special_names.get(col.key, index) if isinstance(col, Column) else 2000,
46-
col.copy(), # type: ignore[attr-defined]
47-
)
48-
for index, col in enumerate(op.columns)
49-
]
50-
columns = [col for _, col in sorted(cols_by_key, key=operator.itemgetter(0))] # pyright: ignore[reportUnknownVariableType,reportUnknownArgumentType,reportUnknownLambdaType]
51-
return ops.CreateTableOp(
52-
op.table_name,
53-
columns, # pyright: ignore[reportUnknownArgumentType]
54-
schema=op.schema,
55-
# TODO: Remove when https://github.com/sqlalchemy/alembic/issues/1193 is fixed # noqa: FIX002
56-
_namespace_metadata=op._namespace_metadata, # noqa: SLF001 # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
57-
**op.kw,
58-
)
59-
60-
6123
def run_migrations_offline() -> None:
6224
"""Run migrations in 'offline' mode.
6325

0 commit comments

Comments
 (0)