This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# orm/query.py
|
||||
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -134,7 +134,6 @@ if TYPE_CHECKING:
|
||||
from ..sql._typing import _TypedColumnClauseArgument as _TCCA
|
||||
from ..sql.base import CacheableOptions
|
||||
from ..sql.base import ExecutableOption
|
||||
from ..sql.dml import UpdateBase
|
||||
from ..sql.elements import ColumnElement
|
||||
from ..sql.elements import Label
|
||||
from ..sql.selectable import _ForUpdateOfArgument
|
||||
@@ -167,6 +166,7 @@ class Query(
|
||||
Executable,
|
||||
Generic[_T],
|
||||
):
|
||||
|
||||
"""ORM-level SQL construction object.
|
||||
|
||||
.. legacy:: The ORM :class:`.Query` object is a legacy construct
|
||||
@@ -205,9 +205,9 @@ class Query(
|
||||
|
||||
_memoized_select_entities = ()
|
||||
|
||||
_compile_options: Union[Type[CacheableOptions], CacheableOptions] = (
|
||||
ORMCompileState.default_compile_options
|
||||
)
|
||||
_compile_options: Union[
|
||||
Type[CacheableOptions], CacheableOptions
|
||||
] = ORMCompileState.default_compile_options
|
||||
|
||||
_with_options: Tuple[ExecutableOption, ...]
|
||||
load_options = QueryContext.default_load_options + {
|
||||
@@ -493,7 +493,7 @@ class Query(
|
||||
return cast("Select[_T]", self.statement)
|
||||
|
||||
@property
|
||||
def statement(self) -> Union[Select[_T], FromStatement[_T], UpdateBase]:
|
||||
def statement(self) -> Union[Select[_T], FromStatement[_T]]:
|
||||
"""The full SELECT statement represented by this Query.
|
||||
|
||||
The statement by default will not have disambiguating labels
|
||||
@@ -521,8 +521,6 @@ class Query(
|
||||
# from there, it starts to look much like Query itself won't be
|
||||
# passed into the execute process and won't generate its own cache
|
||||
# key; this will all occur in terms of the ORM-enabled Select.
|
||||
stmt: Union[Select[_T], FromStatement[_T], UpdateBase]
|
||||
|
||||
if not self._compile_options._set_base_alias:
|
||||
# if we don't have legacy top level aliasing features in use
|
||||
# then convert to a future select() directly
|
||||
@@ -675,38 +673,41 @@ class Query(
|
||||
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
|
||||
class Part(Base):
|
||||
__tablename__ = "part"
|
||||
__tablename__ = 'part'
|
||||
part = Column(String, primary_key=True)
|
||||
sub_part = Column(String, primary_key=True)
|
||||
quantity = Column(Integer)
|
||||
|
||||
|
||||
included_parts = (
|
||||
session.query(Part.sub_part, Part.part, Part.quantity)
|
||||
.filter(Part.part == "our part")
|
||||
.cte(name="included_parts", recursive=True)
|
||||
)
|
||||
included_parts = session.query(
|
||||
Part.sub_part,
|
||||
Part.part,
|
||||
Part.quantity).\
|
||||
filter(Part.part=="our part").\
|
||||
cte(name="included_parts", recursive=True)
|
||||
|
||||
incl_alias = aliased(included_parts, name="pr")
|
||||
parts_alias = aliased(Part, name="p")
|
||||
included_parts = included_parts.union_all(
|
||||
session.query(
|
||||
parts_alias.sub_part, parts_alias.part, parts_alias.quantity
|
||||
).filter(parts_alias.part == incl_alias.c.sub_part)
|
||||
)
|
||||
parts_alias.sub_part,
|
||||
parts_alias.part,
|
||||
parts_alias.quantity).\
|
||||
filter(parts_alias.part==incl_alias.c.sub_part)
|
||||
)
|
||||
|
||||
q = session.query(
|
||||
included_parts.c.sub_part,
|
||||
func.sum(included_parts.c.quantity).label("total_quantity"),
|
||||
).group_by(included_parts.c.sub_part)
|
||||
included_parts.c.sub_part,
|
||||
func.sum(included_parts.c.quantity).
|
||||
label('total_quantity')
|
||||
).\
|
||||
group_by(included_parts.c.sub_part)
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`_sql.Select.cte` - v2 equivalent method.
|
||||
|
||||
""" # noqa: E501
|
||||
"""
|
||||
return (
|
||||
self.enable_eagerloads(False)
|
||||
._get_select_statement_only()
|
||||
@@ -731,17 +732,20 @@ class Query(
|
||||
)
|
||||
|
||||
@overload
|
||||
def as_scalar( # type: ignore[overload-overlap]
|
||||
def as_scalar(
|
||||
self: Query[Tuple[_MAYBE_ENTITY]],
|
||||
) -> ScalarSelect[_MAYBE_ENTITY]: ...
|
||||
) -> ScalarSelect[_MAYBE_ENTITY]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def as_scalar(
|
||||
self: Query[Tuple[_NOT_ENTITY]],
|
||||
) -> ScalarSelect[_NOT_ENTITY]: ...
|
||||
) -> ScalarSelect[_NOT_ENTITY]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def as_scalar(self) -> ScalarSelect[Any]: ...
|
||||
def as_scalar(self) -> ScalarSelect[Any]:
|
||||
...
|
||||
|
||||
@util.deprecated(
|
||||
"1.4",
|
||||
@@ -759,15 +763,18 @@ class Query(
|
||||
@overload
|
||||
def scalar_subquery(
|
||||
self: Query[Tuple[_MAYBE_ENTITY]],
|
||||
) -> ScalarSelect[Any]: ...
|
||||
) -> ScalarSelect[Any]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def scalar_subquery(
|
||||
self: Query[Tuple[_NOT_ENTITY]],
|
||||
) -> ScalarSelect[_NOT_ENTITY]: ...
|
||||
) -> ScalarSelect[_NOT_ENTITY]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def scalar_subquery(self) -> ScalarSelect[Any]: ...
|
||||
def scalar_subquery(self) -> ScalarSelect[Any]:
|
||||
...
|
||||
|
||||
def scalar_subquery(self) -> ScalarSelect[Any]:
|
||||
"""Return the full SELECT statement represented by this
|
||||
@@ -792,7 +799,7 @@ class Query(
|
||||
)
|
||||
|
||||
@property
|
||||
def selectable(self) -> Union[Select[_T], FromStatement[_T], UpdateBase]:
|
||||
def selectable(self) -> Union[Select[_T], FromStatement[_T]]:
|
||||
"""Return the :class:`_expression.Select` object emitted by this
|
||||
:class:`_query.Query`.
|
||||
|
||||
@@ -803,9 +810,7 @@ class Query(
|
||||
"""
|
||||
return self.__clause_element__()
|
||||
|
||||
def __clause_element__(
|
||||
self,
|
||||
) -> Union[Select[_T], FromStatement[_T], UpdateBase]:
|
||||
def __clause_element__(self) -> Union[Select[_T], FromStatement[_T]]:
|
||||
return (
|
||||
self._with_compile_options(
|
||||
_enable_eagerloads=False, _render_for_subquery=True
|
||||
@@ -817,12 +822,14 @@ class Query(
|
||||
@overload
|
||||
def only_return_tuples(
|
||||
self: Query[_O], value: Literal[True]
|
||||
) -> RowReturningQuery[Tuple[_O]]: ...
|
||||
) -> RowReturningQuery[Tuple[_O]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def only_return_tuples(
|
||||
self: Query[_O], value: Literal[False]
|
||||
) -> Query[_O]: ...
|
||||
) -> Query[_O]:
|
||||
...
|
||||
|
||||
@_generative
|
||||
def only_return_tuples(self, value: bool) -> Query[Any]:
|
||||
@@ -943,7 +950,9 @@ class Query(
|
||||
:attr:`_query.Query.statement` using :meth:`.Session.execute`::
|
||||
|
||||
result = session.execute(
|
||||
query.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL).statement
|
||||
query
|
||||
.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
|
||||
.statement
|
||||
)
|
||||
|
||||
.. versionadded:: 1.4
|
||||
@@ -1052,7 +1061,8 @@ class Query(
|
||||
|
||||
some_object = session.query(VersionedFoo).get((5, 10))
|
||||
|
||||
some_object = session.query(VersionedFoo).get({"id": 5, "version_id": 10})
|
||||
some_object = session.query(VersionedFoo).get(
|
||||
{"id": 5, "version_id": 10})
|
||||
|
||||
:meth:`_query.Query.get` is special in that it provides direct
|
||||
access to the identity map of the owning :class:`.Session`.
|
||||
@@ -1118,7 +1128,7 @@ class Query(
|
||||
|
||||
:return: The object instance, or ``None``.
|
||||
|
||||
""" # noqa: E501
|
||||
"""
|
||||
self._no_criterion_assertion("get", order_by=False, distinct=False)
|
||||
|
||||
# we still implement _get_impl() so that baked query can override
|
||||
@@ -1465,13 +1475,15 @@ class Query(
|
||||
return None
|
||||
|
||||
@overload
|
||||
def with_entities(self, _entity: _EntityType[_O]) -> Query[_O]: ...
|
||||
def with_entities(self, _entity: _EntityType[_O]) -> Query[_O]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
self,
|
||||
_colexpr: roles.TypedColumnsClauseRole[_T],
|
||||
) -> RowReturningQuery[Tuple[_T]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T]]:
|
||||
...
|
||||
|
||||
# START OVERLOADED FUNCTIONS self.with_entities RowReturningQuery 2-8
|
||||
|
||||
@@ -1481,12 +1493,14 @@ class Query(
|
||||
@overload
|
||||
def with_entities(
|
||||
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
@@ -1495,7 +1509,8 @@ class Query(
|
||||
__ent1: _TCCA[_T1],
|
||||
__ent2: _TCCA[_T2],
|
||||
__ent3: _TCCA[_T3],
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
@@ -1505,7 +1520,8 @@ class Query(
|
||||
__ent2: _TCCA[_T2],
|
||||
__ent3: _TCCA[_T3],
|
||||
__ent4: _TCCA[_T4],
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
@@ -1516,7 +1532,8 @@ class Query(
|
||||
__ent3: _TCCA[_T3],
|
||||
__ent4: _TCCA[_T4],
|
||||
__ent5: _TCCA[_T5],
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
@@ -1528,7 +1545,8 @@ class Query(
|
||||
__ent4: _TCCA[_T4],
|
||||
__ent5: _TCCA[_T5],
|
||||
__ent6: _TCCA[_T6],
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
@@ -1541,14 +1559,16 @@ class Query(
|
||||
__ent5: _TCCA[_T5],
|
||||
__ent6: _TCCA[_T6],
|
||||
__ent7: _TCCA[_T7],
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]: ...
|
||||
) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
|
||||
...
|
||||
|
||||
# END OVERLOADED FUNCTIONS self.with_entities
|
||||
|
||||
@overload
|
||||
def with_entities(
|
||||
self, *entities: _ColumnsClauseArgument[Any]
|
||||
) -> Query[Any]: ...
|
||||
) -> Query[Any]:
|
||||
...
|
||||
|
||||
@_generative
|
||||
def with_entities(
|
||||
@@ -1562,22 +1582,19 @@ class Query(
|
||||
|
||||
# Users, filtered on some arbitrary criterion
|
||||
# and then ordered by related email address
|
||||
q = (
|
||||
session.query(User)
|
||||
.join(User.address)
|
||||
.filter(User.name.like("%ed%"))
|
||||
.order_by(Address.email)
|
||||
)
|
||||
q = session.query(User).\
|
||||
join(User.address).\
|
||||
filter(User.name.like('%ed%')).\
|
||||
order_by(Address.email)
|
||||
|
||||
# given *only* User.id==5, Address.email, and 'q', what
|
||||
# would the *next* User in the result be ?
|
||||
subq = (
|
||||
q.with_entities(Address.email)
|
||||
.order_by(None)
|
||||
.filter(User.id == 5)
|
||||
.subquery()
|
||||
)
|
||||
q = q.join((subq, subq.c.email < Address.email)).limit(1)
|
||||
subq = q.with_entities(Address.email).\
|
||||
order_by(None).\
|
||||
filter(User.id==5).\
|
||||
subquery()
|
||||
q = q.join((subq, subq.c.email < Address.email)).\
|
||||
limit(1)
|
||||
|
||||
.. seealso::
|
||||
|
||||
@@ -1673,11 +1690,9 @@ class Query(
|
||||
def filter_something(criterion):
|
||||
def transform(q):
|
||||
return q.filter(criterion)
|
||||
|
||||
return transform
|
||||
|
||||
|
||||
q = q.with_transformation(filter_something(x == 5))
|
||||
q = q.with_transformation(filter_something(x==5))
|
||||
|
||||
This allows ad-hoc recipes to be created for :class:`_query.Query`
|
||||
objects.
|
||||
@@ -1714,12 +1729,13 @@ class Query(
|
||||
schema_translate_map: Optional[SchemaTranslateMapType] = ...,
|
||||
populate_existing: bool = False,
|
||||
autoflush: bool = False,
|
||||
preserve_rowcount: bool = False,
|
||||
**opt: Any,
|
||||
) -> Self: ...
|
||||
) -> Self:
|
||||
...
|
||||
|
||||
@overload
|
||||
def execution_options(self, **opt: Any) -> Self: ...
|
||||
def execution_options(self, **opt: Any) -> Self:
|
||||
...
|
||||
|
||||
@_generative
|
||||
def execution_options(self, **kwargs: Any) -> Self:
|
||||
@@ -1794,15 +1810,9 @@ class Query(
|
||||
|
||||
E.g.::
|
||||
|
||||
q = (
|
||||
sess.query(User)
|
||||
.populate_existing()
|
||||
.with_for_update(nowait=True, of=User)
|
||||
)
|
||||
q = sess.query(User).populate_existing().with_for_update(nowait=True, of=User)
|
||||
|
||||
The above query on a PostgreSQL backend will render like:
|
||||
|
||||
.. sourcecode:: sql
|
||||
The above query on a PostgreSQL backend will render like::
|
||||
|
||||
SELECT users.id AS users_id FROM users FOR UPDATE OF users NOWAIT
|
||||
|
||||
@@ -1880,13 +1890,14 @@ class Query(
|
||||
|
||||
e.g.::
|
||||
|
||||
session.query(MyClass).filter(MyClass.name == "some name")
|
||||
session.query(MyClass).filter(MyClass.name == 'some name')
|
||||
|
||||
Multiple criteria may be specified as comma separated; the effect
|
||||
is that they will be joined together using the :func:`.and_`
|
||||
function::
|
||||
|
||||
session.query(MyClass).filter(MyClass.name == "some name", MyClass.id > 5)
|
||||
session.query(MyClass).\
|
||||
filter(MyClass.name == 'some name', MyClass.id > 5)
|
||||
|
||||
The criterion is any SQL expression object applicable to the
|
||||
WHERE clause of a select. String expressions are coerced
|
||||
@@ -1899,7 +1910,7 @@ class Query(
|
||||
|
||||
:meth:`_sql.Select.where` - v2 equivalent method.
|
||||
|
||||
""" # noqa: E501
|
||||
"""
|
||||
for crit in list(criterion):
|
||||
crit = coercions.expect(
|
||||
roles.WhereHavingRole, crit, apply_propagate_attrs=self
|
||||
@@ -1967,13 +1978,14 @@ class Query(
|
||||
|
||||
e.g.::
|
||||
|
||||
session.query(MyClass).filter_by(name="some name")
|
||||
session.query(MyClass).filter_by(name = 'some name')
|
||||
|
||||
Multiple criteria may be specified as comma separated; the effect
|
||||
is that they will be joined together using the :func:`.and_`
|
||||
function::
|
||||
|
||||
session.query(MyClass).filter_by(name="some name", id=5)
|
||||
session.query(MyClass).\
|
||||
filter_by(name = 'some name', id = 5)
|
||||
|
||||
The keyword expressions are extracted from the primary
|
||||
entity of the query, or the last entity that was the
|
||||
@@ -2100,12 +2112,10 @@ class Query(
|
||||
HAVING criterion makes it possible to use filters on aggregate
|
||||
functions like COUNT, SUM, AVG, MAX, and MIN, eg.::
|
||||
|
||||
q = (
|
||||
session.query(User.id)
|
||||
.join(User.addresses)
|
||||
.group_by(User.id)
|
||||
.having(func.count(Address.id) > 2)
|
||||
)
|
||||
q = session.query(User.id).\
|
||||
join(User.addresses).\
|
||||
group_by(User.id).\
|
||||
having(func.count(Address.id) > 2)
|
||||
|
||||
.. seealso::
|
||||
|
||||
@@ -2129,8 +2139,8 @@ class Query(
|
||||
|
||||
e.g.::
|
||||
|
||||
q1 = sess.query(SomeClass).filter(SomeClass.foo == "bar")
|
||||
q2 = sess.query(SomeClass).filter(SomeClass.bar == "foo")
|
||||
q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar')
|
||||
q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo')
|
||||
|
||||
q3 = q1.union(q2)
|
||||
|
||||
@@ -2139,9 +2149,7 @@ class Query(
|
||||
|
||||
x.union(y).union(z).all()
|
||||
|
||||
will nest on each ``union()``, and produces:
|
||||
|
||||
.. sourcecode:: sql
|
||||
will nest on each ``union()``, and produces::
|
||||
|
||||
SELECT * FROM (SELECT * FROM (SELECT * FROM X UNION
|
||||
SELECT * FROM y) UNION SELECT * FROM Z)
|
||||
@@ -2150,9 +2158,7 @@ class Query(
|
||||
|
||||
x.union(y, z).all()
|
||||
|
||||
produces:
|
||||
|
||||
.. sourcecode:: sql
|
||||
produces::
|
||||
|
||||
SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y UNION
|
||||
SELECT * FROM Z)
|
||||
@@ -2264,9 +2270,7 @@ class Query(
|
||||
q = session.query(User).join(User.addresses)
|
||||
|
||||
Where above, the call to :meth:`_query.Query.join` along
|
||||
``User.addresses`` will result in SQL approximately equivalent to:
|
||||
|
||||
.. sourcecode:: sql
|
||||
``User.addresses`` will result in SQL approximately equivalent to::
|
||||
|
||||
SELECT user.id, user.name
|
||||
FROM user JOIN address ON user.id = address.user_id
|
||||
@@ -2279,12 +2283,10 @@ class Query(
|
||||
calls may be used. The relationship-bound attribute implies both
|
||||
the left and right side of the join at once::
|
||||
|
||||
q = (
|
||||
session.query(User)
|
||||
.join(User.orders)
|
||||
.join(Order.items)
|
||||
.join(Item.keywords)
|
||||
)
|
||||
q = session.query(User).\
|
||||
join(User.orders).\
|
||||
join(Order.items).\
|
||||
join(Item.keywords)
|
||||
|
||||
.. note:: as seen in the above example, **the order in which each
|
||||
call to the join() method occurs is important**. Query would not,
|
||||
@@ -2323,7 +2325,7 @@ class Query(
|
||||
as the ON clause to be passed explicitly. A example that includes
|
||||
a SQL expression as the ON clause is as follows::
|
||||
|
||||
q = session.query(User).join(Address, User.id == Address.user_id)
|
||||
q = session.query(User).join(Address, User.id==Address.user_id)
|
||||
|
||||
The above form may also use a relationship-bound attribute as the
|
||||
ON clause as well::
|
||||
@@ -2338,13 +2340,11 @@ class Query(
|
||||
a1 = aliased(Address)
|
||||
a2 = aliased(Address)
|
||||
|
||||
q = (
|
||||
session.query(User)
|
||||
.join(a1, User.addresses)
|
||||
.join(a2, User.addresses)
|
||||
.filter(a1.email_address == "ed@foo.com")
|
||||
.filter(a2.email_address == "ed@bar.com")
|
||||
)
|
||||
q = session.query(User).\
|
||||
join(a1, User.addresses).\
|
||||
join(a2, User.addresses).\
|
||||
filter(a1.email_address=='ed@foo.com').\
|
||||
filter(a2.email_address=='ed@bar.com')
|
||||
|
||||
The relationship-bound calling form can also specify a target entity
|
||||
using the :meth:`_orm.PropComparator.of_type` method; a query
|
||||
@@ -2353,13 +2353,11 @@ class Query(
|
||||
a1 = aliased(Address)
|
||||
a2 = aliased(Address)
|
||||
|
||||
q = (
|
||||
session.query(User)
|
||||
.join(User.addresses.of_type(a1))
|
||||
.join(User.addresses.of_type(a2))
|
||||
.filter(a1.email_address == "ed@foo.com")
|
||||
.filter(a2.email_address == "ed@bar.com")
|
||||
)
|
||||
q = session.query(User).\
|
||||
join(User.addresses.of_type(a1)).\
|
||||
join(User.addresses.of_type(a2)).\
|
||||
filter(a1.email_address == 'ed@foo.com').\
|
||||
filter(a2.email_address == 'ed@bar.com')
|
||||
|
||||
**Augmenting Built-in ON Clauses**
|
||||
|
||||
@@ -2370,7 +2368,7 @@ class Query(
|
||||
with the default criteria using AND::
|
||||
|
||||
q = session.query(User).join(
|
||||
User.addresses.and_(Address.email_address != "foo@bar.com")
|
||||
User.addresses.and_(Address.email_address != 'foo@bar.com')
|
||||
)
|
||||
|
||||
.. versionadded:: 1.4
|
||||
@@ -2383,28 +2381,29 @@ class Query(
|
||||
appropriate ``.subquery()`` method in order to make a subquery
|
||||
out of a query::
|
||||
|
||||
subq = (
|
||||
session.query(Address)
|
||||
.filter(Address.email_address == "ed@foo.com")
|
||||
.subquery()
|
||||
subq = session.query(Address).\
|
||||
filter(Address.email_address == 'ed@foo.com').\
|
||||
subquery()
|
||||
|
||||
|
||||
q = session.query(User).join(
|
||||
subq, User.id == subq.c.user_id
|
||||
)
|
||||
|
||||
|
||||
q = session.query(User).join(subq, User.id == subq.c.user_id)
|
||||
|
||||
Joining to a subquery in terms of a specific relationship and/or
|
||||
target entity may be achieved by linking the subquery to the
|
||||
entity using :func:`_orm.aliased`::
|
||||
|
||||
subq = (
|
||||
session.query(Address)
|
||||
.filter(Address.email_address == "ed@foo.com")
|
||||
.subquery()
|
||||
)
|
||||
subq = session.query(Address).\
|
||||
filter(Address.email_address == 'ed@foo.com').\
|
||||
subquery()
|
||||
|
||||
address_subq = aliased(Address, subq)
|
||||
|
||||
q = session.query(User).join(User.addresses.of_type(address_subq))
|
||||
q = session.query(User).join(
|
||||
User.addresses.of_type(address_subq)
|
||||
)
|
||||
|
||||
|
||||
**Controlling what to Join From**
|
||||
|
||||
@@ -2412,16 +2411,11 @@ class Query(
|
||||
:class:`_query.Query` is not in line with what we want to join from,
|
||||
the :meth:`_query.Query.select_from` method may be used::
|
||||
|
||||
q = (
|
||||
session.query(Address)
|
||||
.select_from(User)
|
||||
.join(User.addresses)
|
||||
.filter(User.name == "ed")
|
||||
)
|
||||
q = session.query(Address).select_from(User).\
|
||||
join(User.addresses).\
|
||||
filter(User.name == 'ed')
|
||||
|
||||
Which will produce SQL similar to:
|
||||
|
||||
.. sourcecode:: sql
|
||||
Which will produce SQL similar to::
|
||||
|
||||
SELECT address.* FROM user
|
||||
JOIN address ON user.id=address.user_id
|
||||
@@ -2525,16 +2519,11 @@ class Query(
|
||||
|
||||
A typical example::
|
||||
|
||||
q = (
|
||||
session.query(Address)
|
||||
.select_from(User)
|
||||
.join(User.addresses)
|
||||
.filter(User.name == "ed")
|
||||
)
|
||||
q = session.query(Address).select_from(User).\
|
||||
join(User.addresses).\
|
||||
filter(User.name == 'ed')
|
||||
|
||||
Which produces SQL equivalent to:
|
||||
|
||||
.. sourcecode:: sql
|
||||
Which produces SQL equivalent to::
|
||||
|
||||
SELECT address.* FROM user
|
||||
JOIN address ON user.id=address.user_id
|
||||
@@ -2787,10 +2776,11 @@ class Query(
|
||||
def one(self) -> _T:
|
||||
"""Return exactly one result or raise an exception.
|
||||
|
||||
Raises :class:`_exc.NoResultFound` if the query selects no rows.
|
||||
Raises :class:`_exc.MultipleResultsFound` if multiple object identities
|
||||
are returned, or if multiple rows are returned for a query that returns
|
||||
only scalar values as opposed to full identity-mapped entities.
|
||||
Raises ``sqlalchemy.orm.exc.NoResultFound`` if the query selects
|
||||
no rows. Raises ``sqlalchemy.orm.exc.MultipleResultsFound``
|
||||
if multiple object identities are returned, or if multiple
|
||||
rows are returned for a query that returns only scalar values
|
||||
as opposed to full identity-mapped entities.
|
||||
|
||||
Calling :meth:`.one` results in an execution of the underlying query.
|
||||
|
||||
@@ -2810,7 +2800,7 @@ class Query(
|
||||
def scalar(self) -> Any:
|
||||
"""Return the first element of the first result or None
|
||||
if no rows present. If multiple rows are returned,
|
||||
raises :class:`_exc.MultipleResultsFound`.
|
||||
raises MultipleResultsFound.
|
||||
|
||||
>>> session.query(Item).scalar()
|
||||
<Item>
|
||||
@@ -2896,7 +2886,7 @@ class Query(
|
||||
|
||||
Format is a list of dictionaries::
|
||||
|
||||
user_alias = aliased(User, name="user2")
|
||||
user_alias = aliased(User, name='user2')
|
||||
q = sess.query(User, User.id, user_alias)
|
||||
|
||||
# this expression:
|
||||
@@ -2905,26 +2895,26 @@ class Query(
|
||||
# would return:
|
||||
[
|
||||
{
|
||||
"name": "User",
|
||||
"type": User,
|
||||
"aliased": False,
|
||||
"expr": User,
|
||||
"entity": User,
|
||||
'name':'User',
|
||||
'type':User,
|
||||
'aliased':False,
|
||||
'expr':User,
|
||||
'entity': User
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"type": Integer(),
|
||||
"aliased": False,
|
||||
"expr": User.id,
|
||||
"entity": User,
|
||||
'name':'id',
|
||||
'type':Integer(),
|
||||
'aliased':False,
|
||||
'expr':User.id,
|
||||
'entity': User
|
||||
},
|
||||
{
|
||||
"name": "user2",
|
||||
"type": User,
|
||||
"aliased": True,
|
||||
"expr": user_alias,
|
||||
"entity": user_alias,
|
||||
},
|
||||
'name':'user2',
|
||||
'type':User,
|
||||
'aliased':True,
|
||||
'expr':user_alias,
|
||||
'entity': user_alias
|
||||
}
|
||||
]
|
||||
|
||||
.. seealso::
|
||||
@@ -2969,7 +2959,6 @@ class Query(
|
||||
context = QueryContext(
|
||||
compile_state,
|
||||
compile_state.statement,
|
||||
compile_state.statement,
|
||||
self._params,
|
||||
self.session,
|
||||
self.load_options,
|
||||
@@ -3033,12 +3022,10 @@ class Query(
|
||||
|
||||
e.g.::
|
||||
|
||||
q = session.query(User).filter(User.name == "fred")
|
||||
q = session.query(User).filter(User.name == 'fred')
|
||||
session.query(q.exists())
|
||||
|
||||
Producing SQL similar to:
|
||||
|
||||
.. sourcecode:: sql
|
||||
Producing SQL similar to::
|
||||
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM users WHERE users.name = :name_1
|
||||
@@ -3087,9 +3074,7 @@ class Query(
|
||||
r"""Return a count of rows this the SQL formed by this :class:`Query`
|
||||
would return.
|
||||
|
||||
This generates the SQL for this Query as follows:
|
||||
|
||||
.. sourcecode:: sql
|
||||
This generates the SQL for this Query as follows::
|
||||
|
||||
SELECT count(1) AS count_1 FROM (
|
||||
SELECT <rest of query follows...>
|
||||
@@ -3129,7 +3114,8 @@ class Query(
|
||||
|
||||
# return count of user "id" grouped
|
||||
# by "name"
|
||||
session.query(func.count(User.id)).group_by(User.name)
|
||||
session.query(func.count(User.id)).\
|
||||
group_by(User.name)
|
||||
|
||||
from sqlalchemy import distinct
|
||||
|
||||
@@ -3147,9 +3133,7 @@ class Query(
|
||||
)
|
||||
|
||||
def delete(
|
||||
self,
|
||||
synchronize_session: SynchronizeSessionArgument = "auto",
|
||||
delete_args: Optional[Dict[Any, Any]] = None,
|
||||
self, synchronize_session: SynchronizeSessionArgument = "auto"
|
||||
) -> int:
|
||||
r"""Perform a DELETE with an arbitrary WHERE clause.
|
||||
|
||||
@@ -3157,11 +3141,11 @@ class Query(
|
||||
|
||||
E.g.::
|
||||
|
||||
sess.query(User).filter(User.age == 25).delete(synchronize_session=False)
|
||||
sess.query(User).filter(User.age == 25).\
|
||||
delete(synchronize_session=False)
|
||||
|
||||
sess.query(User).filter(User.age == 25).delete(
|
||||
synchronize_session="evaluate"
|
||||
)
|
||||
sess.query(User).filter(User.age == 25).\
|
||||
delete(synchronize_session='evaluate')
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -3174,13 +3158,6 @@ class Query(
|
||||
:ref:`orm_expression_update_delete` for a discussion of these
|
||||
strategies.
|
||||
|
||||
:param delete_args: Optional dictionary, if present will be passed
|
||||
to the underlying :func:`_expression.delete` construct as the ``**kw``
|
||||
for the object. May be used to pass dialect-specific arguments such
|
||||
as ``mysql_limit``.
|
||||
|
||||
.. versionadded:: 2.0.37
|
||||
|
||||
:return: the count of rows matched as returned by the database's
|
||||
"row count" feature.
|
||||
|
||||
@@ -3188,9 +3165,9 @@ class Query(
|
||||
|
||||
:ref:`orm_expression_update_delete`
|
||||
|
||||
""" # noqa: E501
|
||||
"""
|
||||
|
||||
bulk_del = BulkDelete(self, delete_args)
|
||||
bulk_del = BulkDelete(self)
|
||||
if self.dispatch.before_compile_delete:
|
||||
for fn in self.dispatch.before_compile_delete:
|
||||
new_query = fn(bulk_del.query, bulk_del)
|
||||
@@ -3200,10 +3177,6 @@ class Query(
|
||||
self = bulk_del.query
|
||||
|
||||
delete_ = sql.delete(*self._raw_columns) # type: ignore
|
||||
|
||||
if delete_args:
|
||||
delete_ = delete_.with_dialect_options(**delete_args)
|
||||
|
||||
delete_._where_criteria = self._where_criteria
|
||||
result: CursorResult[Any] = self.session.execute(
|
||||
delete_,
|
||||
@@ -3230,13 +3203,11 @@ class Query(
|
||||
|
||||
E.g.::
|
||||
|
||||
sess.query(User).filter(User.age == 25).update(
|
||||
{User.age: User.age - 10}, synchronize_session=False
|
||||
)
|
||||
sess.query(User).filter(User.age == 25).\
|
||||
update({User.age: User.age - 10}, synchronize_session=False)
|
||||
|
||||
sess.query(User).filter(User.age == 25).update(
|
||||
{"age": User.age - 10}, synchronize_session="evaluate"
|
||||
)
|
||||
sess.query(User).filter(User.age == 25).\
|
||||
update({"age": User.age - 10}, synchronize_session='evaluate')
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -3259,8 +3230,9 @@ class Query(
|
||||
strategies.
|
||||
|
||||
:param update_args: Optional dictionary, if present will be passed
|
||||
to the underlying :func:`_expression.update` construct as the ``**kw``
|
||||
for the object. May be used to pass dialect-specific arguments such
|
||||
to the underlying :func:`_expression.update`
|
||||
construct as the ``**kw`` for
|
||||
the object. May be used to pass dialect-specific arguments such
|
||||
as ``mysql_limit``, as well as other special arguments such as
|
||||
:paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order`.
|
||||
|
||||
@@ -3339,16 +3311,13 @@ class Query(
|
||||
ORMCompileState._get_plugin_class_for_plugin(stmt, "orm"),
|
||||
)
|
||||
|
||||
return compile_state_cls._create_orm_context(
|
||||
stmt, toplevel=True, compiler=None
|
||||
)
|
||||
return compile_state_cls.create_for_statement(stmt, None)
|
||||
|
||||
def _compile_context(self, for_statement: bool = False) -> QueryContext:
|
||||
compile_state = self._compile_state(for_statement=for_statement)
|
||||
context = QueryContext(
|
||||
compile_state,
|
||||
compile_state.statement,
|
||||
compile_state.statement,
|
||||
self._params,
|
||||
self.session,
|
||||
self.load_options,
|
||||
@@ -3437,14 +3406,6 @@ class BulkUpdate(BulkUD):
|
||||
class BulkDelete(BulkUD):
|
||||
"""BulkUD which handles DELETEs."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
query: Query[Any],
|
||||
delete_kwargs: Optional[Dict[Any, Any]],
|
||||
):
|
||||
super().__init__(query)
|
||||
self.delete_kwargs = delete_kwargs
|
||||
|
||||
|
||||
class RowReturningQuery(Query[Row[_TP]]):
|
||||
if TYPE_CHECKING:
|
||||
|
||||
Reference in New Issue
Block a user