From 96b10c71d433fa8eace37cff106531412f64e337 Mon Sep 17 00:00:00 2001 From: Yossi <54272821+Apakottur@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:04:44 +0000 Subject: [PATCH] Add Mapped() support for values() --- .../sql/_selectable_constructors.py | 4 ++- lib/sqlalchemy/sql/selectable.py | 4 +-- .../sql/_selectable_constructors.py | 32 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 test/typing/plain_files/sql/_selectable_constructors.py diff --git a/lib/sqlalchemy/sql/_selectable_constructors.py b/lib/sqlalchemy/sql/_selectable_constructors.py index 129806204b..0a11688a38 100644 --- a/lib/sqlalchemy/sql/_selectable_constructors.py +++ b/lib/sqlalchemy/sql/_selectable_constructors.py @@ -33,10 +33,12 @@ from .selectable import Values from ..util.typing import TupleAny from ..util.typing import Unpack + if TYPE_CHECKING: from ._typing import _FromClauseArgument from ._typing import _OnClauseArgument from ._typing import _SelectStatementForCompoundArgument + from ._typing import _DMLColumnArgument from ._typing import _T0 from ._typing import _T1 from ._typing import _T2 @@ -684,7 +686,7 @@ def union_all( def values( - *columns: ColumnClause[Any], + *columns: _DMLColumnArgument[Any], name: Optional[str] = None, literal_binds: bool = False, ) -> Values: diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index 48fc75e3a1..e2872bf7bb 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -123,6 +123,7 @@ if TYPE_CHECKING: from ._typing import _NOT_ENTITY from ._typing import _OnClauseArgument from ._typing import _SelectStatementForCompoundArgument + from ._typing import _DMLColumnArgument from ._typing import _T0 from ._typing import _T1 from ._typing import _T2 @@ -3367,7 +3368,7 @@ class Values(roles.InElementRole, HasCTE, Generative, LateralFromClause): def __init__( self, - *columns: ColumnClause[Any], + *columns: _DMLColumnArgument[Any], name: Optional[str] = None, literal_binds: bool = False, ): @@ -7117,7 +7118,6 @@ class Exists(UnaryExpression[bool]): self, fn: Callable[[Select[Unpack[TupleAny]]], Select[Unpack[TupleAny]]], ) -> ScalarSelect[Any]: - assert isinstance(self.element, ScalarSelect) element = self.element.element if not isinstance(element, Select): diff --git a/test/typing/plain_files/sql/_selectable_constructors.py b/test/typing/plain_files/sql/_selectable_constructors.py new file mode 100644 index 0000000000..177e639875 --- /dev/null +++ b/test/typing/plain_files/sql/_selectable_constructors.py @@ -0,0 +1,32 @@ +from sqlalchemy import update +from sqlalchemy import values + +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column + + +class DbBase(DeclarativeBase): + pass + + +class MyTable(DbBase): + __tablename__ = "my_table" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + + +update_values = values( + MyTable.id, + MyTable.name, + name="update_values", +).data([(1, "Alice"), (2, "Bob")]) + +query = ( + update(MyTable) + .values( + { + MyTable.name: update_values.c.name, + } + ) + .where(MyTable.id == update_values.c.id) +) -- 2.47.3