diff options
Diffstat (limited to 'alembic')
-rw-r--r-- | alembic/ddl/postgresql.py | 51 | ||||
-rw-r--r-- | alembic/op.pyi | 49 | ||||
-rw-r--r-- | alembic/operations/ops.py | 49 |
3 files changed, 100 insertions, 49 deletions
diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py index 5fb981d..cc0488b 100644 --- a/alembic/ddl/postgresql.py +++ b/alembic/ddl/postgresql.py @@ -25,6 +25,7 @@ from sqlalchemy.sql import operators from sqlalchemy.sql.elements import ColumnClause from sqlalchemy.sql.elements import TextClause from sqlalchemy.sql.elements import UnaryExpression +from sqlalchemy.sql.functions import FunctionElement from sqlalchemy.types import NULLTYPE from .base import alter_column @@ -662,22 +663,15 @@ def _exclude_constraint( ("name", render._render_gen_name(autogen_context, constraint.name)) ) - if alter: + def do_expr_where_opts(): args = [ - repr(render._render_gen_name(autogen_context, constraint.name)) + "(%s, %r)" + % ( + _render_potential_column(sqltext, autogen_context), + opstring, + ) + for sqltext, name, opstring in constraint._render_exprs # type:ignore[attr-defined] # noqa ] - if not has_batch: - args += [repr(render._ident(constraint.table.name))] - args.extend( - [ - "(%s, %r)" - % ( - _render_potential_column(sqltext, autogen_context), - opstring, - ) - for sqltext, name, opstring in constraint._render_exprs # type:ignore[attr-defined] # noqa - ] - ) if constraint.where is not None: args.append( "where=%s" @@ -686,24 +680,21 @@ def _exclude_constraint( ) ) args.extend(["%s=%r" % (k, v) for k, v in opts]) + return args + + if alter: + args = [ + repr(render._render_gen_name(autogen_context, constraint.name)) + ] + if not has_batch: + args += [repr(render._ident(constraint.table.name))] + args.extend(do_expr_where_opts()) return "%(prefix)screate_exclude_constraint(%(args)s)" % { "prefix": render._alembic_autogenerate_prefix(autogen_context), "args": ", ".join(args), } else: - args = [ - "(%s, %r)" - % (_render_potential_column(sqltext, autogen_context), opstring) - for sqltext, name, opstring in constraint._render_exprs - ] - if constraint.where is not None: - args.append( - "where=%s" - % render._render_potential_expr( - constraint.where, autogen_context - ) - ) - args.extend(["%s=%r" % (k, v) for k, v in opts]) + args = do_expr_where_opts() return "%(prefix)sExcludeConstraint(%(args)s)" % { "prefix": _postgresql_autogenerate_prefix(autogen_context), "args": ", ".join(args), @@ -711,7 +702,7 @@ def _exclude_constraint( def _render_potential_column( - value: Union[ColumnClause, Column, TextClause], + value: Union[ColumnClause, Column, TextClause, FunctionElement], autogen_context: AutogenContext, ) -> str: if isinstance(value, ColumnClause): @@ -727,5 +718,7 @@ def _render_potential_column( } else: return render._render_potential_expr( - value, autogen_context, wrap_in_text=isinstance(value, TextClause) + value, + autogen_context, + wrap_in_text=isinstance(value, (TextClause, FunctionElement)), ) diff --git a/alembic/op.pyi b/alembic/op.pyi index 01edb61..535b2d5 100644 --- a/alembic/op.pyi +++ b/alembic/op.pyi @@ -53,11 +53,39 @@ def add_column( op.add_column("organization", Column("name", String())) - The provided :class:`~sqlalchemy.schema.Column` object can also - specify a :class:`~sqlalchemy.schema.ForeignKey`, referencing - a remote table name. Alembic will automatically generate a stub - "referenced" table and emit a second ALTER statement in order - to add the constraint separately:: + The :meth:`.Operations.add_column` method typically corresponds + to the SQL command "ALTER TABLE... ADD COLUMN". Within the scope + of this command, the column's name, datatype, nullability, + and optional server-generated defaults may be indicated. + + .. note:: + + With the exception of NOT NULL constraints or single-column FOREIGN KEY + constraints, other kinds of constraints such as PRIMARY KEY, UNIQUE or + CHECK constraints **cannot** be generated using this method; for these + constraints, refer to operations such as + :meth:`.Operations.create_primary_key` and + :meth:`.Operations.create_check_constraint`. In particular, the + following :class:`~sqlalchemy.schema.Column` parameters are + **ignored**: + + * :paramref:`~sqlalchemy.schema.Column.primary_key` - SQL databases + typically do not support an ALTER operation that can add individual + columns one at a time to an existing primary key constraint, + therefore it's less ambiguous to use the + :meth:`.Operations.create_primary_key` method, which assumes no + existing primary key constraint is present. + * :paramref:`~sqlalchemy.schema.Column.unique` - use the + :meth:`.Operations.create_unique_constraint` method + * :paramref:`~sqlalchemy.schema.Column.index` - use the + :meth:`.Operations.create_index` method + + + The provided :class:`~sqlalchemy.schema.Column` object may include a + :class:`~sqlalchemy.schema.ForeignKey` constraint directive, + referencing a remote table name. For this specific type of constraint, + Alembic will automatically emit a second ALTER statement in order to + add the single-column FOREIGN KEY constraint separately:: from alembic import op from sqlalchemy import Column, INTEGER, ForeignKey @@ -67,11 +95,12 @@ def add_column( Column("account_id", INTEGER, ForeignKey("accounts.id")), ) - Note that this statement uses the :class:`~sqlalchemy.schema.Column` - construct as is from the SQLAlchemy library. In particular, - default values to be created on the database side are - specified using the ``server_default`` parameter, and not - ``default`` which only specifies Python-side defaults:: + The column argument passed to :meth:`.Operations.add_column` is a + :class:`~sqlalchemy.schema.Column` construct, used in the same way it's + used in SQLAlchemy. In particular, values or functions to be indicated + as producing the column's default value on the database side are + specified using the ``server_default`` parameter, and not ``default`` + which only specifies Python-side defaults:: from alembic import op from sqlalchemy import Column, TIMESTAMP, func diff --git a/alembic/operations/ops.py b/alembic/operations/ops.py index f95ab70..0295ab3 100644 --- a/alembic/operations/ops.py +++ b/alembic/operations/ops.py @@ -2012,11 +2012,39 @@ class AddColumnOp(AlterTableOp): op.add_column("organization", Column("name", String())) - The provided :class:`~sqlalchemy.schema.Column` object can also - specify a :class:`~sqlalchemy.schema.ForeignKey`, referencing - a remote table name. Alembic will automatically generate a stub - "referenced" table and emit a second ALTER statement in order - to add the constraint separately:: + The :meth:`.Operations.add_column` method typically corresponds + to the SQL command "ALTER TABLE... ADD COLUMN". Within the scope + of this command, the column's name, datatype, nullability, + and optional server-generated defaults may be indicated. + + .. note:: + + With the exception of NOT NULL constraints or single-column FOREIGN + KEY constraints, other kinds of constraints such as PRIMARY KEY, + UNIQUE or CHECK constraints **cannot** be generated using this + method; for these constraints, refer to operations such as + :meth:`.Operations.create_primary_key` and + :meth:`.Operations.create_check_constraint`. In particular, the + following :class:`~sqlalchemy.schema.Column` parameters are + **ignored**: + + * :paramref:`~sqlalchemy.schema.Column.primary_key` - SQL databases + typically do not support an ALTER operation that can add + individual columns one at a time to an existing primary key + constraint, therefore it's less ambiguous to use the + :meth:`.Operations.create_primary_key` method, which assumes no + existing primary key constraint is present. + * :paramref:`~sqlalchemy.schema.Column.unique` - use the + :meth:`.Operations.create_unique_constraint` method + * :paramref:`~sqlalchemy.schema.Column.index` - use the + :meth:`.Operations.create_index` method + + + The provided :class:`~sqlalchemy.schema.Column` object may include a + :class:`~sqlalchemy.schema.ForeignKey` constraint directive, + referencing a remote table name. For this specific type of constraint, + Alembic will automatically emit a second ALTER statement in order to + add the single-column FOREIGN KEY constraint separately:: from alembic import op from sqlalchemy import Column, INTEGER, ForeignKey @@ -2026,11 +2054,12 @@ class AddColumnOp(AlterTableOp): Column("account_id", INTEGER, ForeignKey("accounts.id")), ) - Note that this statement uses the :class:`~sqlalchemy.schema.Column` - construct as is from the SQLAlchemy library. In particular, - default values to be created on the database side are - specified using the ``server_default`` parameter, and not - ``default`` which only specifies Python-side defaults:: + The column argument passed to :meth:`.Operations.add_column` is a + :class:`~sqlalchemy.schema.Column` construct, used in the same way it's + used in SQLAlchemy. In particular, values or functions to be indicated + as producing the column's default value on the database side are + specified using the ``server_default`` parameter, and not ``default`` + which only specifies Python-side defaults:: from alembic import op from sqlalchemy import Column, TIMESTAMP, func |