| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453 |
- # orm/query.py
- # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
- # <see AUTHORS file>
- #
- # This module is part of SQLAlchemy and is released under
- # the MIT License: https://www.opensource.org/licenses/mit-license.php
- """The Query class and support.
- Defines the :class:`_query.Query` class, the central
- construct used by the ORM to construct database queries.
- The :class:`_query.Query` class should not be confused with the
- :class:`_expression.Select` class, which defines database
- SELECT operations at the SQL (non-ORM) level. ``Query`` differs from
- ``Select`` in that it returns ORM-mapped objects and interacts with an
- ORM session, whereas the ``Select`` construct interacts directly with the
- database to return iterable result sets.
- """
- from __future__ import annotations
- import collections.abc as collections_abc
- import operator
- from typing import Any
- from typing import Callable
- from typing import cast
- from typing import Dict
- from typing import Generic
- from typing import Iterable
- from typing import Iterator
- from typing import List
- from typing import Mapping
- from typing import Optional
- from typing import overload
- from typing import Sequence
- from typing import Tuple
- from typing import Type
- from typing import TYPE_CHECKING
- from typing import TypeVar
- from typing import Union
- from . import attributes
- from . import interfaces
- from . import loading
- from . import util as orm_util
- from ._typing import _O
- from .base import _assertions
- from .context import _column_descriptions
- from .context import _determine_last_joined_entity
- from .context import _legacy_filter_by_entity_zero
- from .context import FromStatement
- from .context import ORMCompileState
- from .context import QueryContext
- from .interfaces import ORMColumnDescription
- from .interfaces import ORMColumnsClauseRole
- from .util import AliasedClass
- from .util import object_mapper
- from .util import with_parent
- from .. import exc as sa_exc
- from .. import inspect
- from .. import inspection
- from .. import log
- from .. import sql
- from .. import util
- from ..engine import Result
- from ..engine import Row
- from ..event import dispatcher
- from ..event import EventTarget
- from ..sql import coercions
- from ..sql import expression
- from ..sql import roles
- from ..sql import Select
- from ..sql import util as sql_util
- from ..sql import visitors
- from ..sql._typing import _FromClauseArgument
- from ..sql._typing import _TP
- from ..sql.annotation import SupportsCloneAnnotations
- from ..sql.base import _entity_namespace_key
- from ..sql.base import _generative
- from ..sql.base import _NoArg
- from ..sql.base import Executable
- from ..sql.base import Generative
- from ..sql.elements import BooleanClauseList
- from ..sql.expression import Exists
- from ..sql.selectable import _MemoizedSelectEntities
- from ..sql.selectable import _SelectFromElements
- from ..sql.selectable import ForUpdateArg
- from ..sql.selectable import HasHints
- from ..sql.selectable import HasPrefixes
- from ..sql.selectable import HasSuffixes
- from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
- from ..sql.selectable import SelectLabelStyle
- from ..util.typing import Literal
- from ..util.typing import Self
- if TYPE_CHECKING:
- from ._typing import _EntityType
- from ._typing import _ExternalEntityType
- from ._typing import _InternalEntityType
- from ._typing import SynchronizeSessionArgument
- from .mapper import Mapper
- from .path_registry import PathRegistry
- from .session import _PKIdentityArgument
- from .session import Session
- from .state import InstanceState
- from ..engine.cursor import CursorResult
- from ..engine.interfaces import _ImmutableExecuteOptions
- from ..engine.interfaces import CompiledCacheType
- from ..engine.interfaces import IsolationLevel
- from ..engine.interfaces import SchemaTranslateMapType
- from ..engine.result import FrozenResult
- from ..engine.result import ScalarResult
- from ..sql._typing import _ColumnExpressionArgument
- from ..sql._typing import _ColumnExpressionOrStrLabelArgument
- from ..sql._typing import _ColumnsClauseArgument
- from ..sql._typing import _DMLColumnArgument
- from ..sql._typing import _JoinTargetArgument
- from ..sql._typing import _LimitOffsetType
- from ..sql._typing import _MAYBE_ENTITY
- from ..sql._typing import _no_kw
- from ..sql._typing import _NOT_ENTITY
- from ..sql._typing import _OnClauseArgument
- from ..sql._typing import _PropagateAttrsType
- from ..sql._typing import _T0
- from ..sql._typing import _T1
- from ..sql._typing import _T2
- from ..sql._typing import _T3
- from ..sql._typing import _T4
- from ..sql._typing import _T5
- from ..sql._typing import _T6
- from ..sql._typing import _T7
- 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
- from ..sql.selectable import _JoinTargetElement
- from ..sql.selectable import _SetupJoinsElement
- from ..sql.selectable import Alias
- from ..sql.selectable import CTE
- from ..sql.selectable import ExecutableReturnsRows
- from ..sql.selectable import FromClause
- from ..sql.selectable import ScalarSelect
- from ..sql.selectable import Subquery
- __all__ = ["Query", "QueryContext"]
- _T = TypeVar("_T", bound=Any)
- @inspection._self_inspects
- @log.class_logger
- class Query(
- _SelectFromElements,
- SupportsCloneAnnotations,
- HasPrefixes,
- HasSuffixes,
- HasHints,
- EventTarget,
- log.Identified,
- Generative,
- Executable,
- Generic[_T],
- ):
- """ORM-level SQL construction object.
- .. legacy:: The ORM :class:`.Query` object is a legacy construct
- as of SQLAlchemy 2.0. See the notes at the top of
- :ref:`query_api_toplevel` for an overview, including links to migration
- documentation.
- :class:`_query.Query` objects are normally initially generated using the
- :meth:`~.Session.query` method of :class:`.Session`, and in
- less common cases by instantiating the :class:`_query.Query` directly and
- associating with a :class:`.Session` using the
- :meth:`_query.Query.with_session`
- method.
- """
- # elements that are in Core and can be cached in the same way
- _where_criteria: Tuple[ColumnElement[Any], ...] = ()
- _having_criteria: Tuple[ColumnElement[Any], ...] = ()
- _order_by_clauses: Tuple[ColumnElement[Any], ...] = ()
- _group_by_clauses: Tuple[ColumnElement[Any], ...] = ()
- _limit_clause: Optional[ColumnElement[Any]] = None
- _offset_clause: Optional[ColumnElement[Any]] = None
- _distinct: bool = False
- _distinct_on: Tuple[ColumnElement[Any], ...] = ()
- _for_update_arg: Optional[ForUpdateArg] = None
- _correlate: Tuple[FromClause, ...] = ()
- _auto_correlate: bool = True
- _from_obj: Tuple[FromClause, ...] = ()
- _setup_joins: Tuple[_SetupJoinsElement, ...] = ()
- _label_style: SelectLabelStyle = SelectLabelStyle.LABEL_STYLE_LEGACY_ORM
- _memoized_select_entities = ()
- _compile_options: Union[Type[CacheableOptions], CacheableOptions] = (
- ORMCompileState.default_compile_options
- )
- _with_options: Tuple[ExecutableOption, ...]
- load_options = QueryContext.default_load_options + {
- "_legacy_uniquing": True
- }
- _params: util.immutabledict[str, Any] = util.EMPTY_DICT
- # local Query builder state, not needed for
- # compilation or execution
- _enable_assertions = True
- _statement: Optional[ExecutableReturnsRows] = None
- session: Session
- dispatch: dispatcher[Query[_T]]
- # mirrors that of ClauseElement, used to propagate the "orm"
- # plugin as well as the "subject" of the plugin, e.g. the mapper
- # we are querying against.
- @util.memoized_property
- def _propagate_attrs(self) -> _PropagateAttrsType:
- return util.EMPTY_DICT
- def __init__(
- self,
- entities: Union[
- _ColumnsClauseArgument[Any], Sequence[_ColumnsClauseArgument[Any]]
- ],
- session: Optional[Session] = None,
- ):
- """Construct a :class:`_query.Query` directly.
- E.g.::
- q = Query([User, Address], session=some_session)
- The above is equivalent to::
- q = some_session.query(User, Address)
- :param entities: a sequence of entities and/or SQL expressions.
- :param session: a :class:`.Session` with which the
- :class:`_query.Query`
- will be associated. Optional; a :class:`_query.Query`
- can be associated
- with a :class:`.Session` generatively via the
- :meth:`_query.Query.with_session` method as well.
- .. seealso::
- :meth:`.Session.query`
- :meth:`_query.Query.with_session`
- """
- # session is usually present. There's one case in subqueryloader
- # where it stores a Query without a Session and also there are tests
- # for the query(Entity).with_session(session) API which is likely in
- # some old recipes, however these are legacy as select() can now be
- # used.
- self.session = session # type: ignore
- self._set_entities(entities)
- def _set_propagate_attrs(self, values: Mapping[str, Any]) -> Self:
- self._propagate_attrs = util.immutabledict(values)
- return self
- def _set_entities(
- self,
- entities: Union[
- _ColumnsClauseArgument[Any], Iterable[_ColumnsClauseArgument[Any]]
- ],
- ) -> None:
- self._raw_columns = [
- coercions.expect(
- roles.ColumnsClauseRole,
- ent,
- apply_propagate_attrs=self,
- post_inspect=True,
- )
- for ent in util.to_list(entities)
- ]
- def tuples(self: Query[_O]) -> Query[Tuple[_O]]:
- """return a tuple-typed form of this :class:`.Query`.
- This method invokes the :meth:`.Query.only_return_tuples`
- method with a value of ``True``, which by itself ensures that this
- :class:`.Query` will always return :class:`.Row` objects, even
- if the query is made against a single entity. It then also
- at the typing level will return a "typed" query, if possible,
- that will type result rows as ``Tuple`` objects with typed
- elements.
- This method can be compared to the :meth:`.Result.tuples` method,
- which returns "self", but from a typing perspective returns an object
- that will yield typed ``Tuple`` objects for results. Typing
- takes effect only if this :class:`.Query` object is a typed
- query object already.
- .. versionadded:: 2.0
- .. seealso::
- :meth:`.Result.tuples` - v2 equivalent method.
- """
- return self.only_return_tuples(True) # type: ignore
- def _entity_from_pre_ent_zero(self) -> Optional[_InternalEntityType[Any]]:
- if not self._raw_columns:
- return None
- ent = self._raw_columns[0]
- if "parententity" in ent._annotations:
- return ent._annotations["parententity"] # type: ignore
- elif "bundle" in ent._annotations:
- return ent._annotations["bundle"] # type: ignore
- else:
- # label, other SQL expression
- for element in visitors.iterate(ent):
- if "parententity" in element._annotations:
- return element._annotations["parententity"] # type: ignore # noqa: E501
- else:
- return None
- def _only_full_mapper_zero(self, methname: str) -> Mapper[Any]:
- if (
- len(self._raw_columns) != 1
- or "parententity" not in self._raw_columns[0]._annotations
- or not self._raw_columns[0].is_selectable
- ):
- raise sa_exc.InvalidRequestError(
- "%s() can only be used against "
- "a single mapped class." % methname
- )
- return self._raw_columns[0]._annotations["parententity"] # type: ignore # noqa: E501
- def _set_select_from(
- self, obj: Iterable[_FromClauseArgument], set_base_alias: bool
- ) -> None:
- fa = [
- coercions.expect(
- roles.StrictFromClauseRole,
- elem,
- allow_select=True,
- apply_propagate_attrs=self,
- )
- for elem in obj
- ]
- self._compile_options += {"_set_base_alias": set_base_alias}
- self._from_obj = tuple(fa)
- @_generative
- def _set_lazyload_from(self, state: InstanceState[Any]) -> Self:
- self.load_options += {"_lazy_loaded_from": state}
- return self
- def _get_condition(self) -> None:
- """used by legacy BakedQuery"""
- self._no_criterion_condition("get", order_by=False, distinct=False)
- def _get_existing_condition(self) -> None:
- self._no_criterion_assertion("get", order_by=False, distinct=False)
- def _no_criterion_assertion(
- self, meth: str, order_by: bool = True, distinct: bool = True
- ) -> None:
- if not self._enable_assertions:
- return
- if (
- self._where_criteria
- or self._statement is not None
- or self._from_obj
- or self._setup_joins
- or self._limit_clause is not None
- or self._offset_clause is not None
- or self._group_by_clauses
- or (order_by and self._order_by_clauses)
- or (distinct and self._distinct)
- ):
- raise sa_exc.InvalidRequestError(
- "Query.%s() being called on a "
- "Query with existing criterion. " % meth
- )
- def _no_criterion_condition(
- self, meth: str, order_by: bool = True, distinct: bool = True
- ) -> None:
- self._no_criterion_assertion(meth, order_by, distinct)
- self._from_obj = self._setup_joins = ()
- if self._statement is not None:
- self._compile_options += {"_statement": None}
- self._where_criteria = ()
- self._distinct = False
- self._order_by_clauses = self._group_by_clauses = ()
- def _no_clauseelement_condition(self, meth: str) -> None:
- if not self._enable_assertions:
- return
- if self._order_by_clauses:
- raise sa_exc.InvalidRequestError(
- "Query.%s() being called on a "
- "Query with existing criterion. " % meth
- )
- self._no_criterion_condition(meth)
- def _no_statement_condition(self, meth: str) -> None:
- if not self._enable_assertions:
- return
- if self._statement is not None:
- raise sa_exc.InvalidRequestError(
- (
- "Query.%s() being called on a Query with an existing full "
- "statement - can't apply criterion."
- )
- % meth
- )
- def _no_limit_offset(self, meth: str) -> None:
- if not self._enable_assertions:
- return
- if self._limit_clause is not None or self._offset_clause is not None:
- raise sa_exc.InvalidRequestError(
- "Query.%s() being called on a Query which already has LIMIT "
- "or OFFSET applied. Call %s() before limit() or offset() "
- "are applied." % (meth, meth)
- )
- @property
- def _has_row_limiting_clause(self) -> bool:
- return (
- self._limit_clause is not None or self._offset_clause is not None
- )
- def _get_options(
- self,
- populate_existing: Optional[bool] = None,
- version_check: Optional[bool] = None,
- only_load_props: Optional[Sequence[str]] = None,
- refresh_state: Optional[InstanceState[Any]] = None,
- identity_token: Optional[Any] = None,
- ) -> Self:
- load_options: Dict[str, Any] = {}
- compile_options: Dict[str, Any] = {}
- if version_check:
- load_options["_version_check"] = version_check
- if populate_existing:
- load_options["_populate_existing"] = populate_existing
- if refresh_state:
- load_options["_refresh_state"] = refresh_state
- compile_options["_for_refresh_state"] = True
- if only_load_props:
- compile_options["_only_load_props"] = frozenset(only_load_props)
- if identity_token:
- load_options["_identity_token"] = identity_token
- if load_options:
- self.load_options += load_options
- if compile_options:
- self._compile_options += compile_options
- return self
- def _clone(self, **kw: Any) -> Self:
- return self._generate()
- def _get_select_statement_only(self) -> Select[_T]:
- if self._statement is not None:
- raise sa_exc.InvalidRequestError(
- "Can't call this method on a Query that uses from_statement()"
- )
- return cast("Select[_T]", self.statement)
- @property
- def statement(self) -> Union[Select[_T], FromStatement[_T], UpdateBase]:
- """The full SELECT statement represented by this Query.
- The statement by default will not have disambiguating labels
- applied to the construct unless with_labels(True) is called
- first.
- """
- # .statement can return the direct future.Select() construct here, as
- # long as we are not using subsequent adaption features that
- # are made against raw entities, e.g. from_self(), with_polymorphic(),
- # select_entity_from(). If these features are being used, then
- # the Select() we return will not have the correct .selected_columns
- # collection and will not embed in subsequent queries correctly.
- # We could find a way to make this collection "correct", however
- # this would not be too different from doing the full compile as
- # we are doing in any case, the Select() would still not have the
- # proper state for other attributes like whereclause, order_by,
- # and these features are all deprecated in any case.
- #
- # for these reasons, Query is not a Select, it remains an ORM
- # object for which __clause_element__() must be called in order for
- # it to provide a real expression object.
- #
- # 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
- stmt = self._statement_20(for_statement=True)
- else:
- stmt = self._compile_state(for_statement=True).statement
- if self._params:
- stmt = stmt.params(self._params)
- return stmt
- def _final_statement(self, legacy_query_style: bool = True) -> Select[Any]:
- """Return the 'final' SELECT statement for this :class:`.Query`.
- This is used by the testing suite only and is fairly inefficient.
- This is the Core-only select() that will be rendered by a complete
- compilation of this query, and is what .statement used to return
- in 1.3.
- """
- q = self._clone()
- return q._compile_state(
- use_legacy_query_style=legacy_query_style
- ).statement # type: ignore
- def _statement_20(
- self, for_statement: bool = False, use_legacy_query_style: bool = True
- ) -> Union[Select[_T], FromStatement[_T]]:
- # TODO: this event needs to be deprecated, as it currently applies
- # only to ORM query and occurs at this spot that is now more
- # or less an artificial spot
- if self.dispatch.before_compile:
- for fn in self.dispatch.before_compile:
- new_query = fn(self)
- if new_query is not None and new_query is not self:
- self = new_query
- if not fn._bake_ok: # type: ignore
- self._compile_options += {"_bake_ok": False}
- compile_options = self._compile_options
- compile_options += {
- "_for_statement": for_statement,
- "_use_legacy_query_style": use_legacy_query_style,
- }
- stmt: Union[Select[_T], FromStatement[_T]]
- if self._statement is not None:
- stmt = FromStatement(self._raw_columns, self._statement)
- stmt.__dict__.update(
- _with_options=self._with_options,
- _with_context_options=self._with_context_options,
- _compile_options=compile_options,
- _execution_options=self._execution_options,
- _propagate_attrs=self._propagate_attrs,
- )
- else:
- # Query / select() internal attributes are 99% cross-compatible
- stmt = Select._create_raw_select(**self.__dict__)
- stmt.__dict__.update(
- _label_style=self._label_style,
- _compile_options=compile_options,
- _propagate_attrs=self._propagate_attrs,
- )
- stmt.__dict__.pop("session", None)
- # ensure the ORM context is used to compile the statement, even
- # if it has no ORM entities. This is so ORM-only things like
- # _legacy_joins are picked up that wouldn't be picked up by the
- # Core statement context
- if "compile_state_plugin" not in stmt._propagate_attrs:
- stmt._propagate_attrs = stmt._propagate_attrs.union(
- {"compile_state_plugin": "orm", "plugin_subject": None}
- )
- return stmt
- def subquery(
- self,
- name: Optional[str] = None,
- with_labels: bool = False,
- reduce_columns: bool = False,
- ) -> Subquery:
- """Return the full SELECT statement represented by
- this :class:`_query.Query`, embedded within an
- :class:`_expression.Alias`.
- Eager JOIN generation within the query is disabled.
- .. seealso::
- :meth:`_sql.Select.subquery` - v2 comparable method.
- :param name: string name to be assigned as the alias;
- this is passed through to :meth:`_expression.FromClause.alias`.
- If ``None``, a name will be deterministically generated
- at compile time.
- :param with_labels: if True, :meth:`.with_labels` will be called
- on the :class:`_query.Query` first to apply table-qualified labels
- to all columns.
- :param reduce_columns: if True,
- :meth:`_expression.Select.reduce_columns` will
- be called on the resulting :func:`_expression.select` construct,
- to remove same-named columns where one also refers to the other
- via foreign key or WHERE clause equivalence.
- """
- q = self.enable_eagerloads(False)
- if with_labels:
- q = q.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
- stmt = q._get_select_statement_only()
- if TYPE_CHECKING:
- assert isinstance(stmt, Select)
- if reduce_columns:
- stmt = stmt.reduce_columns()
- return stmt.subquery(name=name)
- def cte(
- self,
- name: Optional[str] = None,
- recursive: bool = False,
- nesting: bool = False,
- ) -> CTE:
- r"""Return the full SELECT statement represented by this
- :class:`_query.Query` represented as a common table expression (CTE).
- Parameters and usage are the same as those of the
- :meth:`_expression.SelectBase.cte` method; see that method for
- further details.
- Here is the `PostgreSQL WITH
- RECURSIVE example
- <https://www.postgresql.org/docs/current/static/queries-with.html>`_.
- Note that, in this example, the ``included_parts`` cte and the
- ``incl_alias`` alias of it are Core selectables, which
- means the columns are accessed via the ``.c.`` attribute. The
- ``parts_alias`` object is an :func:`_orm.aliased` instance of the
- ``Part`` entity, so column-mapped attributes are available
- directly::
- from sqlalchemy.orm import aliased
- class Part(Base):
- __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)
- )
- 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)
- )
- q = session.query(
- 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()
- .cte(name=name, recursive=recursive, nesting=nesting)
- )
- def label(self, name: Optional[str]) -> Label[Any]:
- """Return the full SELECT statement represented by this
- :class:`_query.Query`, converted
- to a scalar subquery with a label of the given name.
- .. seealso::
- :meth:`_sql.Select.label` - v2 comparable method.
- """
- return (
- self.enable_eagerloads(False)
- ._get_select_statement_only()
- .label(name)
- )
- @overload
- def as_scalar( # type: ignore[overload-overlap]
- self: Query[Tuple[_MAYBE_ENTITY]],
- ) -> ScalarSelect[_MAYBE_ENTITY]: ...
- @overload
- def as_scalar(
- self: Query[Tuple[_NOT_ENTITY]],
- ) -> ScalarSelect[_NOT_ENTITY]: ...
- @overload
- def as_scalar(self) -> ScalarSelect[Any]: ...
- @util.deprecated(
- "1.4",
- "The :meth:`_query.Query.as_scalar` method is deprecated and will be "
- "removed in a future release. Please refer to "
- ":meth:`_query.Query.scalar_subquery`.",
- )
- def as_scalar(self) -> ScalarSelect[Any]:
- """Return the full SELECT statement represented by this
- :class:`_query.Query`, converted to a scalar subquery.
- """
- return self.scalar_subquery()
- @overload
- def scalar_subquery(
- self: Query[Tuple[_MAYBE_ENTITY]],
- ) -> ScalarSelect[Any]: ...
- @overload
- def scalar_subquery(
- self: Query[Tuple[_NOT_ENTITY]],
- ) -> ScalarSelect[_NOT_ENTITY]: ...
- @overload
- def scalar_subquery(self) -> ScalarSelect[Any]: ...
- def scalar_subquery(self) -> ScalarSelect[Any]:
- """Return the full SELECT statement represented by this
- :class:`_query.Query`, converted to a scalar subquery.
- Analogous to
- :meth:`sqlalchemy.sql.expression.SelectBase.scalar_subquery`.
- .. versionchanged:: 1.4 The :meth:`_query.Query.scalar_subquery`
- method replaces the :meth:`_query.Query.as_scalar` method.
- .. seealso::
- :meth:`_sql.Select.scalar_subquery` - v2 comparable method.
- """
- return (
- self.enable_eagerloads(False)
- ._get_select_statement_only()
- .scalar_subquery()
- )
- @property
- def selectable(self) -> Union[Select[_T], FromStatement[_T], UpdateBase]:
- """Return the :class:`_expression.Select` object emitted by this
- :class:`_query.Query`.
- Used for :func:`_sa.inspect` compatibility, this is equivalent to::
- query.enable_eagerloads(False).with_labels().statement
- """
- return self.__clause_element__()
- def __clause_element__(
- self,
- ) -> Union[Select[_T], FromStatement[_T], UpdateBase]:
- return (
- self._with_compile_options(
- _enable_eagerloads=False, _render_for_subquery=True
- )
- .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
- .statement
- )
- @overload
- def only_return_tuples(
- self: Query[_O], value: Literal[True]
- ) -> RowReturningQuery[Tuple[_O]]: ...
- @overload
- def only_return_tuples(
- self: Query[_O], value: Literal[False]
- ) -> Query[_O]: ...
- @_generative
- def only_return_tuples(self, value: bool) -> Query[Any]:
- """When set to True, the query results will always be a
- :class:`.Row` object.
- This can change a query that normally returns a single entity
- as a scalar to return a :class:`.Row` result in all cases.
- .. seealso::
- :meth:`.Query.tuples` - returns tuples, but also at the typing
- level will type results as ``Tuple``.
- :meth:`_query.Query.is_single_entity`
- :meth:`_engine.Result.tuples` - v2 comparable method.
- """
- self.load_options += dict(_only_return_tuples=value)
- return self
- @property
- def is_single_entity(self) -> bool:
- """Indicates if this :class:`_query.Query`
- returns tuples or single entities.
- Returns True if this query returns a single entity for each instance
- in its result list, and False if this query returns a tuple of entities
- for each result.
- .. versionadded:: 1.3.11
- .. seealso::
- :meth:`_query.Query.only_return_tuples`
- """
- return (
- not self.load_options._only_return_tuples
- and len(self._raw_columns) == 1
- and "parententity" in self._raw_columns[0]._annotations
- and isinstance(
- self._raw_columns[0]._annotations["parententity"],
- ORMColumnsClauseRole,
- )
- )
- @_generative
- def enable_eagerloads(self, value: bool) -> Self:
- """Control whether or not eager joins and subqueries are
- rendered.
- When set to False, the returned Query will not render
- eager joins regardless of :func:`~sqlalchemy.orm.joinedload`,
- :func:`~sqlalchemy.orm.subqueryload` options
- or mapper-level ``lazy='joined'``/``lazy='subquery'``
- configurations.
- This is used primarily when nesting the Query's
- statement into a subquery or other
- selectable, or when using :meth:`_query.Query.yield_per`.
- """
- self._compile_options += {"_enable_eagerloads": value}
- return self
- @_generative
- def _with_compile_options(self, **opt: Any) -> Self:
- self._compile_options += opt
- return self
- @util.became_legacy_20(
- ":meth:`_orm.Query.with_labels` and :meth:`_orm.Query.apply_labels`",
- alternative="Use set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) "
- "instead.",
- )
- def with_labels(self) -> Self:
- return self.set_label_style(
- SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL
- )
- apply_labels = with_labels
- @property
- def get_label_style(self) -> SelectLabelStyle:
- """
- Retrieve the current label style.
- .. versionadded:: 1.4
- .. seealso::
- :meth:`_sql.Select.get_label_style` - v2 equivalent method.
- """
- return self._label_style
- def set_label_style(self, style: SelectLabelStyle) -> Self:
- """Apply column labels to the return value of Query.statement.
- Indicates that this Query's `statement` accessor should return
- a SELECT statement that applies labels to all columns in the
- form <tablename>_<columnname>; this is commonly used to
- disambiguate columns from multiple tables which have the same
- name.
- When the `Query` actually issues SQL to load rows, it always
- uses column labeling.
- .. note:: The :meth:`_query.Query.set_label_style` method *only* applies
- the output of :attr:`_query.Query.statement`, and *not* to any of
- the result-row invoking systems of :class:`_query.Query` itself,
- e.g.
- :meth:`_query.Query.first`, :meth:`_query.Query.all`, etc.
- To execute
- a query using :meth:`_query.Query.set_label_style`, invoke the
- :attr:`_query.Query.statement` using :meth:`.Session.execute`::
- result = session.execute(
- query.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL).statement
- )
- .. versionadded:: 1.4
- .. seealso::
- :meth:`_sql.Select.set_label_style` - v2 equivalent method.
- """ # noqa
- if self._label_style is not style:
- self = self._generate()
- self._label_style = style
- return self
- @_generative
- def enable_assertions(self, value: bool) -> Self:
- """Control whether assertions are generated.
- When set to False, the returned Query will
- not assert its state before certain operations,
- including that LIMIT/OFFSET has not been applied
- when filter() is called, no criterion exists
- when get() is called, and no "from_statement()"
- exists when filter()/order_by()/group_by() etc.
- is called. This more permissive mode is used by
- custom Query subclasses to specify criterion or
- other modifiers outside of the usual usage patterns.
- Care should be taken to ensure that the usage
- pattern is even possible. A statement applied
- by from_statement() will override any criterion
- set by filter() or order_by(), for example.
- """
- self._enable_assertions = value
- return self
- @property
- def whereclause(self) -> Optional[ColumnElement[bool]]:
- """A readonly attribute which returns the current WHERE criterion for
- this Query.
- This returned value is a SQL expression construct, or ``None`` if no
- criterion has been established.
- .. seealso::
- :attr:`_sql.Select.whereclause` - v2 equivalent property.
- """
- return BooleanClauseList._construct_for_whereclause(
- self._where_criteria
- )
- @_generative
- def _with_current_path(self, path: PathRegistry) -> Self:
- """indicate that this query applies to objects loaded
- within a certain path.
- Used by deferred loaders (see strategies.py) which transfer
- query options from an originating query to a newly generated
- query intended for the deferred load.
- """
- self._compile_options += {"_current_path": path}
- return self
- @_generative
- def yield_per(self, count: int) -> Self:
- r"""Yield only ``count`` rows at a time.
- The purpose of this method is when fetching very large result sets
- (> 10K rows), to batch results in sub-collections and yield them
- out partially, so that the Python interpreter doesn't need to declare
- very large areas of memory which is both time consuming and leads
- to excessive memory use. The performance from fetching hundreds of
- thousands of rows can often double when a suitable yield-per setting
- (e.g. approximately 1000) is used, even with DBAPIs that buffer
- rows (which are most).
- As of SQLAlchemy 1.4, the :meth:`_orm.Query.yield_per` method is
- equivalent to using the ``yield_per`` execution option at the ORM
- level. See the section :ref:`orm_queryguide_yield_per` for further
- background on this option.
- .. seealso::
- :ref:`orm_queryguide_yield_per`
- """
- self.load_options += {"_yield_per": count}
- return self
- @util.became_legacy_20(
- ":meth:`_orm.Query.get`",
- alternative="The method is now available as :meth:`_orm.Session.get`",
- )
- def get(self, ident: _PKIdentityArgument) -> Optional[Any]:
- """Return an instance based on the given primary key identifier,
- or ``None`` if not found.
- E.g.::
- my_user = session.query(User).get(5)
- some_object = session.query(VersionedFoo).get((5, 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`.
- If the given primary key identifier is present
- in the local identity map, the object is returned
- directly from this collection and no SQL is emitted,
- unless the object has been marked fully expired.
- If not present,
- a SELECT is performed in order to locate the object.
- :meth:`_query.Query.get` also will perform a check if
- the object is present in the identity map and
- marked as expired - a SELECT
- is emitted to refresh the object as well as to
- ensure that the row is still present.
- If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised.
- :meth:`_query.Query.get` is only used to return a single
- mapped instance, not multiple instances or
- individual column constructs, and strictly
- on a single primary key value. The originating
- :class:`_query.Query` must be constructed in this way,
- i.e. against a single mapped entity,
- with no additional filtering criterion. Loading
- options via :meth:`_query.Query.options` may be applied
- however, and will be used if the object is not
- yet locally present.
- :param ident: A scalar, tuple, or dictionary representing the
- primary key. For a composite (e.g. multiple column) primary key,
- a tuple or dictionary should be passed.
- For a single-column primary key, the scalar calling form is typically
- the most expedient. If the primary key of a row is the value "5",
- the call looks like::
- my_object = query.get(5)
- The tuple form contains primary key values typically in
- the order in which they correspond to the mapped
- :class:`_schema.Table`
- object's primary key columns, or if the
- :paramref:`_orm.Mapper.primary_key` configuration parameter were
- used, in
- the order used for that parameter. For example, if the primary key
- of a row is represented by the integer
- digits "5, 10" the call would look like::
- my_object = query.get((5, 10))
- The dictionary form should include as keys the mapped attribute names
- corresponding to each element of the primary key. If the mapped class
- has the attributes ``id``, ``version_id`` as the attributes which
- store the object's primary key value, the call would look like::
- my_object = query.get({"id": 5, "version_id": 10})
- .. versionadded:: 1.3 the :meth:`_query.Query.get`
- method now optionally
- accepts a dictionary of attribute names to values in order to
- indicate a primary key identifier.
- :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
- # it
- return self._get_impl(ident, loading.load_on_pk_identity)
- def _get_impl(
- self,
- primary_key_identity: _PKIdentityArgument,
- db_load_fn: Callable[..., Any],
- identity_token: Optional[Any] = None,
- ) -> Optional[Any]:
- mapper = self._only_full_mapper_zero("get")
- return self.session._get_impl(
- mapper,
- primary_key_identity,
- db_load_fn,
- populate_existing=self.load_options._populate_existing,
- with_for_update=self._for_update_arg,
- options=self._with_options,
- identity_token=identity_token,
- execution_options=self._execution_options,
- )
- @property
- def lazy_loaded_from(self) -> Optional[InstanceState[Any]]:
- """An :class:`.InstanceState` that is using this :class:`_query.Query`
- for a lazy load operation.
- .. deprecated:: 1.4 This attribute should be viewed via the
- :attr:`.ORMExecuteState.lazy_loaded_from` attribute, within
- the context of the :meth:`.SessionEvents.do_orm_execute`
- event.
- .. seealso::
- :attr:`.ORMExecuteState.lazy_loaded_from`
- """
- return self.load_options._lazy_loaded_from # type: ignore
- @property
- def _current_path(self) -> PathRegistry:
- return self._compile_options._current_path # type: ignore
- @_generative
- def correlate(
- self,
- *fromclauses: Union[Literal[None, False], _FromClauseArgument],
- ) -> Self:
- """Return a :class:`.Query` construct which will correlate the given
- FROM clauses to that of an enclosing :class:`.Query` or
- :func:`~.expression.select`.
- The method here accepts mapped classes, :func:`.aliased` constructs,
- and :class:`_orm.Mapper` constructs as arguments, which are resolved
- into expression constructs, in addition to appropriate expression
- constructs.
- The correlation arguments are ultimately passed to
- :meth:`_expression.Select.correlate`
- after coercion to expression constructs.
- The correlation arguments take effect in such cases
- as when :meth:`_query.Query.from_self` is used, or when
- a subquery as returned by :meth:`_query.Query.subquery` is
- embedded in another :func:`_expression.select` construct.
- .. seealso::
- :meth:`_sql.Select.correlate` - v2 equivalent method.
- """
- self._auto_correlate = False
- if fromclauses and fromclauses[0] in {None, False}:
- self._correlate = ()
- else:
- self._correlate = self._correlate + tuple(
- coercions.expect(roles.FromClauseRole, f) for f in fromclauses
- )
- return self
- @_generative
- def autoflush(self, setting: bool) -> Self:
- """Return a Query with a specific 'autoflush' setting.
- As of SQLAlchemy 1.4, the :meth:`_orm.Query.autoflush` method
- is equivalent to using the ``autoflush`` execution option at the
- ORM level. See the section :ref:`orm_queryguide_autoflush` for
- further background on this option.
- """
- self.load_options += {"_autoflush": setting}
- return self
- @_generative
- def populate_existing(self) -> Self:
- """Return a :class:`_query.Query`
- that will expire and refresh all instances
- as they are loaded, or reused from the current :class:`.Session`.
- As of SQLAlchemy 1.4, the :meth:`_orm.Query.populate_existing` method
- is equivalent to using the ``populate_existing`` execution option at
- the ORM level. See the section :ref:`orm_queryguide_populate_existing`
- for further background on this option.
- """
- self.load_options += {"_populate_existing": True}
- return self
- @_generative
- def _with_invoke_all_eagers(self, value: bool) -> Self:
- """Set the 'invoke all eagers' flag which causes joined- and
- subquery loaders to traverse into already-loaded related objects
- and collections.
- Default is that of :attr:`_query.Query._invoke_all_eagers`.
- """
- self.load_options += {"_invoke_all_eagers": value}
- return self
- @util.became_legacy_20(
- ":meth:`_orm.Query.with_parent`",
- alternative="Use the :func:`_orm.with_parent` standalone construct.",
- )
- @util.preload_module("sqlalchemy.orm.relationships")
- def with_parent(
- self,
- instance: object,
- property: Optional[ # noqa: A002
- attributes.QueryableAttribute[Any]
- ] = None,
- from_entity: Optional[_ExternalEntityType[Any]] = None,
- ) -> Self:
- """Add filtering criterion that relates the given instance
- to a child object or collection, using its attribute state
- as well as an established :func:`_orm.relationship()`
- configuration.
- The method uses the :func:`.with_parent` function to generate
- the clause, the result of which is passed to
- :meth:`_query.Query.filter`.
- Parameters are the same as :func:`.with_parent`, with the exception
- that the given property can be None, in which case a search is
- performed against this :class:`_query.Query` object's target mapper.
- :param instance:
- An instance which has some :func:`_orm.relationship`.
- :param property:
- Class bound attribute which indicates
- what relationship from the instance should be used to reconcile the
- parent/child relationship.
- :param from_entity:
- Entity in which to consider as the left side. This defaults to the
- "zero" entity of the :class:`_query.Query` itself.
- """
- relationships = util.preloaded.orm_relationships
- if from_entity:
- entity_zero = inspect(from_entity)
- else:
- entity_zero = _legacy_filter_by_entity_zero(self)
- if property is None:
- # TODO: deprecate, property has to be supplied
- mapper = object_mapper(instance)
- for prop in mapper.iterate_properties:
- if (
- isinstance(prop, relationships.RelationshipProperty)
- and prop.mapper is entity_zero.mapper # type: ignore
- ):
- property = prop # type: ignore # noqa: A001
- break
- else:
- raise sa_exc.InvalidRequestError(
- "Could not locate a property which relates instances "
- "of class '%s' to instances of class '%s'"
- % (
- entity_zero.mapper.class_.__name__, # type: ignore
- instance.__class__.__name__,
- )
- )
- return self.filter(
- with_parent(
- instance,
- property, # type: ignore
- entity_zero.entity, # type: ignore
- )
- )
- @_generative
- def add_entity(
- self,
- entity: _EntityType[Any],
- alias: Optional[Union[Alias, Subquery]] = None,
- ) -> Query[Any]:
- """add a mapped entity to the list of result columns
- to be returned.
- .. seealso::
- :meth:`_sql.Select.add_columns` - v2 comparable method.
- """
- if alias is not None:
- # TODO: deprecate
- entity = AliasedClass(entity, alias)
- self._raw_columns = list(self._raw_columns)
- self._raw_columns.append(
- coercions.expect(
- roles.ColumnsClauseRole, entity, apply_propagate_attrs=self
- )
- )
- return self
- @_generative
- def with_session(self, session: Session) -> Self:
- """Return a :class:`_query.Query` that will use the given
- :class:`.Session`.
- While the :class:`_query.Query`
- object is normally instantiated using the
- :meth:`.Session.query` method, it is legal to build the
- :class:`_query.Query`
- directly without necessarily using a :class:`.Session`. Such a
- :class:`_query.Query` object, or any :class:`_query.Query`
- already associated
- with a different :class:`.Session`, can produce a new
- :class:`_query.Query`
- object associated with a target session using this method::
- from sqlalchemy.orm import Query
- query = Query([MyClass]).filter(MyClass.id == 5)
- result = query.with_session(my_session).one()
- """
- self.session = session
- return self
- def _legacy_from_self(
- self, *entities: _ColumnsClauseArgument[Any]
- ) -> Self:
- # used for query.count() as well as for the same
- # function in BakedQuery, as well as some old tests in test_baked.py.
- fromclause = (
- self.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
- .correlate(None)
- .subquery()
- ._anonymous_fromclause()
- )
- q = self._from_selectable(fromclause)
- if entities:
- q._set_entities(entities)
- return q
- @_generative
- def _set_enable_single_crit(self, val: bool) -> Self:
- self._compile_options += {"_enable_single_crit": val}
- return self
- @_generative
- def _from_selectable(
- self, fromclause: FromClause, set_entity_from: bool = True
- ) -> Self:
- for attr in (
- "_where_criteria",
- "_order_by_clauses",
- "_group_by_clauses",
- "_limit_clause",
- "_offset_clause",
- "_last_joined_entity",
- "_setup_joins",
- "_memoized_select_entities",
- "_distinct",
- "_distinct_on",
- "_having_criteria",
- "_prefixes",
- "_suffixes",
- ):
- self.__dict__.pop(attr, None)
- self._set_select_from([fromclause], set_entity_from)
- self._compile_options += {
- "_enable_single_crit": False,
- }
- return self
- @util.deprecated(
- "1.4",
- ":meth:`_query.Query.values` "
- "is deprecated and will be removed in a "
- "future release. Please use :meth:`_query.Query.with_entities`",
- )
- def values(self, *columns: _ColumnsClauseArgument[Any]) -> Iterable[Any]:
- """Return an iterator yielding result tuples corresponding
- to the given list of columns
- """
- return self._values_no_warn(*columns)
- _values = values
- def _values_no_warn(
- self, *columns: _ColumnsClauseArgument[Any]
- ) -> Iterable[Any]:
- if not columns:
- return iter(())
- q = self._clone().enable_eagerloads(False)
- q._set_entities(columns)
- if not q.load_options._yield_per:
- q.load_options += {"_yield_per": 10}
- return iter(q)
- @util.deprecated(
- "1.4",
- ":meth:`_query.Query.value` "
- "is deprecated and will be removed in a "
- "future release. Please use :meth:`_query.Query.with_entities` "
- "in combination with :meth:`_query.Query.scalar`",
- )
- def value(self, column: _ColumnExpressionArgument[Any]) -> Any:
- """Return a scalar result corresponding to the given
- column expression.
- """
- try:
- return next(self._values_no_warn(column))[0] # type: ignore
- except StopIteration:
- return None
- @overload
- def with_entities(self, _entity: _EntityType[_O]) -> Query[_O]: ...
- @overload
- def with_entities(
- self,
- _colexpr: roles.TypedColumnsClauseRole[_T],
- ) -> RowReturningQuery[Tuple[_T]]: ...
- # START OVERLOADED FUNCTIONS self.with_entities RowReturningQuery 2-8
- # code within this block is **programmatically,
- # statically generated** by tools/generate_tuple_map_overloads.py
- @overload
- def with_entities(
- self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
- ) -> RowReturningQuery[Tuple[_T0, _T1]]: ...
- @overload
- def with_entities(
- self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
- ) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]: ...
- @overload
- def with_entities(
- self,
- __ent0: _TCCA[_T0],
- __ent1: _TCCA[_T1],
- __ent2: _TCCA[_T2],
- __ent3: _TCCA[_T3],
- ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]: ...
- @overload
- def with_entities(
- self,
- __ent0: _TCCA[_T0],
- __ent1: _TCCA[_T1],
- __ent2: _TCCA[_T2],
- __ent3: _TCCA[_T3],
- __ent4: _TCCA[_T4],
- ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
- @overload
- def with_entities(
- self,
- __ent0: _TCCA[_T0],
- __ent1: _TCCA[_T1],
- __ent2: _TCCA[_T2],
- __ent3: _TCCA[_T3],
- __ent4: _TCCA[_T4],
- __ent5: _TCCA[_T5],
- ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ...
- @overload
- def with_entities(
- self,
- __ent0: _TCCA[_T0],
- __ent1: _TCCA[_T1],
- __ent2: _TCCA[_T2],
- __ent3: _TCCA[_T3],
- __ent4: _TCCA[_T4],
- __ent5: _TCCA[_T5],
- __ent6: _TCCA[_T6],
- ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ...
- @overload
- def with_entities(
- self,
- __ent0: _TCCA[_T0],
- __ent1: _TCCA[_T1],
- __ent2: _TCCA[_T2],
- __ent3: _TCCA[_T3],
- __ent4: _TCCA[_T4],
- __ent5: _TCCA[_T5],
- __ent6: _TCCA[_T6],
- __ent7: _TCCA[_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]: ...
- @_generative
- def with_entities(
- self, *entities: _ColumnsClauseArgument[Any], **__kw: Any
- ) -> Query[Any]:
- r"""Return a new :class:`_query.Query`
- replacing the SELECT list with the
- given entities.
- e.g.::
- # 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)
- )
- # 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)
- .. seealso::
- :meth:`_sql.Select.with_only_columns` - v2 comparable method.
- """
- if __kw:
- raise _no_kw()
- # Query has all the same fields as Select for this operation
- # this could in theory be based on a protocol but not sure if it's
- # worth it
- _MemoizedSelectEntities._generate_for_statement(self) # type: ignore
- self._set_entities(entities)
- return self
- @_generative
- def add_columns(
- self, *column: _ColumnExpressionArgument[Any]
- ) -> Query[Any]:
- """Add one or more column expressions to the list
- of result columns to be returned.
- .. seealso::
- :meth:`_sql.Select.add_columns` - v2 comparable method.
- """
- self._raw_columns = list(self._raw_columns)
- self._raw_columns.extend(
- coercions.expect(
- roles.ColumnsClauseRole,
- c,
- apply_propagate_attrs=self,
- post_inspect=True,
- )
- for c in column
- )
- return self
- @util.deprecated(
- "1.4",
- ":meth:`_query.Query.add_column` "
- "is deprecated and will be removed in a "
- "future release. Please use :meth:`_query.Query.add_columns`",
- )
- def add_column(self, column: _ColumnExpressionArgument[Any]) -> Query[Any]:
- """Add a column expression to the list of result columns to be
- returned.
- """
- return self.add_columns(column)
- @_generative
- def options(self, *args: ExecutableOption) -> Self:
- """Return a new :class:`_query.Query` object,
- applying the given list of
- mapper options.
- Most supplied options regard changing how column- and
- relationship-mapped attributes are loaded.
- .. seealso::
- :ref:`loading_columns`
- :ref:`relationship_loader_options`
- """
- opts = tuple(util.flatten_iterator(args))
- if self._compile_options._current_path:
- # opting for lower method overhead for the checks
- for opt in opts:
- if not opt._is_core and opt._is_legacy_option: # type: ignore
- opt.process_query_conditionally(self) # type: ignore
- else:
- for opt in opts:
- if not opt._is_core and opt._is_legacy_option: # type: ignore
- opt.process_query(self) # type: ignore
- self._with_options += opts
- return self
- def with_transformation(
- self, fn: Callable[[Query[Any]], Query[Any]]
- ) -> Query[Any]:
- """Return a new :class:`_query.Query` object transformed by
- the given function.
- E.g.::
- def filter_something(criterion):
- def transform(q):
- return q.filter(criterion)
- return transform
- q = q.with_transformation(filter_something(x == 5))
- This allows ad-hoc recipes to be created for :class:`_query.Query`
- objects.
- """
- return fn(self)
- def get_execution_options(self) -> _ImmutableExecuteOptions:
- """Get the non-SQL options which will take effect during execution.
- .. versionadded:: 1.3
- .. seealso::
- :meth:`_query.Query.execution_options`
- :meth:`_sql.Select.get_execution_options` - v2 comparable method.
- """
- return self._execution_options
- @overload
- def execution_options(
- self,
- *,
- compiled_cache: Optional[CompiledCacheType] = ...,
- logging_token: str = ...,
- isolation_level: IsolationLevel = ...,
- no_parameters: bool = False,
- stream_results: bool = False,
- max_row_buffer: int = ...,
- yield_per: int = ...,
- insertmanyvalues_page_size: int = ...,
- schema_translate_map: Optional[SchemaTranslateMapType] = ...,
- populate_existing: bool = False,
- autoflush: bool = False,
- preserve_rowcount: bool = False,
- **opt: Any,
- ) -> Self: ...
- @overload
- def execution_options(self, **opt: Any) -> Self: ...
- @_generative
- def execution_options(self, **kwargs: Any) -> Self:
- """Set non-SQL options which take effect during execution.
- Options allowed here include all of those accepted by
- :meth:`_engine.Connection.execution_options`, as well as a series
- of ORM specific options:
- ``populate_existing=True`` - equivalent to using
- :meth:`_orm.Query.populate_existing`
- ``autoflush=True|False`` - equivalent to using
- :meth:`_orm.Query.autoflush`
- ``yield_per=<value>`` - equivalent to using
- :meth:`_orm.Query.yield_per`
- Note that the ``stream_results`` execution option is enabled
- automatically if the :meth:`~sqlalchemy.orm.query.Query.yield_per()`
- method or execution option is used.
- .. versionadded:: 1.4 - added ORM options to
- :meth:`_orm.Query.execution_options`
- The execution options may also be specified on a per execution basis
- when using :term:`2.0 style` queries via the
- :paramref:`_orm.Session.execution_options` parameter.
- .. warning:: The
- :paramref:`_engine.Connection.execution_options.stream_results`
- parameter should not be used at the level of individual ORM
- statement executions, as the :class:`_orm.Session` will not track
- objects from different schema translate maps within a single
- session. For multiple schema translate maps within the scope of a
- single :class:`_orm.Session`, see :ref:`examples_sharding`.
- .. seealso::
- :ref:`engine_stream_results`
- :meth:`_query.Query.get_execution_options`
- :meth:`_sql.Select.execution_options` - v2 equivalent method.
- """
- self._execution_options = self._execution_options.union(kwargs)
- return self
- @_generative
- def with_for_update(
- self,
- *,
- nowait: bool = False,
- read: bool = False,
- of: Optional[_ForUpdateOfArgument] = None,
- skip_locked: bool = False,
- key_share: bool = False,
- ) -> Self:
- """return a new :class:`_query.Query`
- with the specified options for the
- ``FOR UPDATE`` clause.
- The behavior of this method is identical to that of
- :meth:`_expression.GenerativeSelect.with_for_update`.
- When called with no arguments,
- the resulting ``SELECT`` statement will have a ``FOR UPDATE`` clause
- appended. When additional arguments are specified, backend-specific
- options such as ``FOR UPDATE NOWAIT`` or ``LOCK IN SHARE MODE``
- can take effect.
- E.g.::
- 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
- SELECT users.id AS users_id FROM users FOR UPDATE OF users NOWAIT
- .. warning::
- Using ``with_for_update`` in the context of eager loading
- relationships is not officially supported or recommended by
- SQLAlchemy and may not work with certain queries on various
- database backends. When ``with_for_update`` is successfully used
- with a query that involves :func:`_orm.joinedload`, SQLAlchemy will
- attempt to emit SQL that locks all involved tables.
- .. note:: It is generally a good idea to combine the use of the
- :meth:`_orm.Query.populate_existing` method when using the
- :meth:`_orm.Query.with_for_update` method. The purpose of
- :meth:`_orm.Query.populate_existing` is to force all the data read
- from the SELECT to be populated into the ORM objects returned,
- even if these objects are already in the :term:`identity map`.
- .. seealso::
- :meth:`_expression.GenerativeSelect.with_for_update`
- - Core level method with
- full argument and behavioral description.
- :meth:`_orm.Query.populate_existing` - overwrites attributes of
- objects already loaded in the identity map.
- """ # noqa: E501
- self._for_update_arg = ForUpdateArg(
- read=read,
- nowait=nowait,
- of=of,
- skip_locked=skip_locked,
- key_share=key_share,
- )
- return self
- @_generative
- def params(
- self, __params: Optional[Dict[str, Any]] = None, **kw: Any
- ) -> Self:
- r"""Add values for bind parameters which may have been
- specified in filter().
- Parameters may be specified using \**kwargs, or optionally a single
- dictionary as the first positional argument. The reason for both is
- that \**kwargs is convenient, however some parameter dictionaries
- contain unicode keys in which case \**kwargs cannot be used.
- """
- if __params:
- kw.update(__params)
- self._params = self._params.union(kw)
- return self
- def where(self, *criterion: _ColumnExpressionArgument[bool]) -> Self:
- """A synonym for :meth:`.Query.filter`.
- .. versionadded:: 1.4
- .. seealso::
- :meth:`_sql.Select.where` - v2 equivalent method.
- """
- return self.filter(*criterion)
- @_generative
- @_assertions(_no_statement_condition, _no_limit_offset)
- def filter(self, *criterion: _ColumnExpressionArgument[bool]) -> Self:
- r"""Apply the given filtering criterion to a copy
- of this :class:`_query.Query`, using SQL expressions.
- e.g.::
- 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)
- The criterion is any SQL expression object applicable to the
- WHERE clause of a select. String expressions are coerced
- into SQL expression constructs via the :func:`_expression.text`
- construct.
- .. seealso::
- :meth:`_query.Query.filter_by` - filter on keyword expressions.
- :meth:`_sql.Select.where` - v2 equivalent method.
- """ # noqa: E501
- for crit in list(criterion):
- crit = coercions.expect(
- roles.WhereHavingRole, crit, apply_propagate_attrs=self
- )
- self._where_criteria += (crit,)
- return self
- @util.memoized_property
- def _last_joined_entity(
- self,
- ) -> Optional[Union[_InternalEntityType[Any], _JoinTargetElement]]:
- if self._setup_joins:
- return _determine_last_joined_entity(
- self._setup_joins,
- )
- else:
- return None
- def _filter_by_zero(self) -> Any:
- """for the filter_by() method, return the target entity for which
- we will attempt to derive an expression from based on string name.
- """
- if self._setup_joins:
- _last_joined_entity = self._last_joined_entity
- if _last_joined_entity is not None:
- return _last_joined_entity
- # discussion related to #7239
- # special check determines if we should try to derive attributes
- # for filter_by() from the "from object", i.e., if the user
- # called query.select_from(some selectable).filter_by(some_attr=value).
- # We don't want to do that in the case that methods like
- # from_self(), select_entity_from(), or a set op like union() were
- # called; while these methods also place a
- # selectable in the _from_obj collection, they also set up
- # the _set_base_alias boolean which turns on the whole "adapt the
- # entity to this selectable" thing, meaning the query still continues
- # to construct itself in terms of the lead entity that was passed
- # to query(), e.g. query(User).from_self() is still in terms of User,
- # and not the subquery that from_self() created. This feature of
- # "implicitly adapt all occurrences of entity X to some arbitrary
- # subquery" is the main thing I am trying to do away with in 2.0 as
- # users should now used aliased() for that, but I can't entirely get
- # rid of it due to query.union() and other set ops relying upon it.
- #
- # compare this to the base Select()._filter_by_zero() which can
- # just return self._from_obj[0] if present, because there is no
- # "_set_base_alias" feature.
- #
- # IOW, this conditional essentially detects if
- # "select_from(some_selectable)" has been called, as opposed to
- # "select_entity_from()", "from_self()"
- # or "union() / some_set_op()".
- if self._from_obj and not self._compile_options._set_base_alias:
- return self._from_obj[0]
- return self._raw_columns[0]
- def filter_by(self, **kwargs: Any) -> Self:
- r"""Apply the given filtering criterion to a copy
- of this :class:`_query.Query`, using keyword expressions.
- e.g.::
- 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)
- The keyword expressions are extracted from the primary
- entity of the query, or the last entity that was the
- target of a call to :meth:`_query.Query.join`.
- .. seealso::
- :meth:`_query.Query.filter` - filter on SQL expressions.
- :meth:`_sql.Select.filter_by` - v2 comparable method.
- """
- from_entity = self._filter_by_zero()
- clauses = [
- _entity_namespace_key(from_entity, key) == value
- for key, value in kwargs.items()
- ]
- return self.filter(*clauses)
- @_generative
- def order_by(
- self,
- __first: Union[
- Literal[None, False, _NoArg.NO_ARG],
- _ColumnExpressionOrStrLabelArgument[Any],
- ] = _NoArg.NO_ARG,
- *clauses: _ColumnExpressionOrStrLabelArgument[Any],
- ) -> Self:
- """Apply one or more ORDER BY criteria to the query and return
- the newly resulting :class:`_query.Query`.
- e.g.::
- q = session.query(Entity).order_by(Entity.id, Entity.name)
- Calling this method multiple times is equivalent to calling it once
- with all the clauses concatenated. All existing ORDER BY criteria may
- be cancelled by passing ``None`` by itself. New ORDER BY criteria may
- then be added by invoking :meth:`_orm.Query.order_by` again, e.g.::
- # will erase all ORDER BY and ORDER BY new_col alone
- q = q.order_by(None).order_by(new_col)
- .. seealso::
- These sections describe ORDER BY in terms of :term:`2.0 style`
- invocation but apply to :class:`_orm.Query` as well:
- :ref:`tutorial_order_by` - in the :ref:`unified_tutorial`
- :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial`
- :meth:`_sql.Select.order_by` - v2 equivalent method.
- """
- for assertion in (self._no_statement_condition, self._no_limit_offset):
- assertion("order_by")
- if not clauses and (__first is None or __first is False):
- self._order_by_clauses = ()
- elif __first is not _NoArg.NO_ARG:
- criterion = tuple(
- coercions.expect(roles.OrderByRole, clause)
- for clause in (__first,) + clauses
- )
- self._order_by_clauses += criterion
- return self
- @_generative
- def group_by(
- self,
- __first: Union[
- Literal[None, False, _NoArg.NO_ARG],
- _ColumnExpressionOrStrLabelArgument[Any],
- ] = _NoArg.NO_ARG,
- *clauses: _ColumnExpressionOrStrLabelArgument[Any],
- ) -> Self:
- """Apply one or more GROUP BY criterion to the query and return
- the newly resulting :class:`_query.Query`.
- All existing GROUP BY settings can be suppressed by
- passing ``None`` - this will suppress any GROUP BY configured
- on mappers as well.
- .. seealso::
- These sections describe GROUP BY in terms of :term:`2.0 style`
- invocation but apply to :class:`_orm.Query` as well:
- :ref:`tutorial_group_by_w_aggregates` - in the
- :ref:`unified_tutorial`
- :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial`
- :meth:`_sql.Select.group_by` - v2 equivalent method.
- """
- for assertion in (self._no_statement_condition, self._no_limit_offset):
- assertion("group_by")
- if not clauses and (__first is None or __first is False):
- self._group_by_clauses = ()
- elif __first is not _NoArg.NO_ARG:
- criterion = tuple(
- coercions.expect(roles.GroupByRole, clause)
- for clause in (__first,) + clauses
- )
- self._group_by_clauses += criterion
- return self
- @_generative
- @_assertions(_no_statement_condition, _no_limit_offset)
- def having(self, *having: _ColumnExpressionArgument[bool]) -> Self:
- r"""Apply a HAVING criterion to the query and return the
- newly resulting :class:`_query.Query`.
- :meth:`_query.Query.having` is used in conjunction with
- :meth:`_query.Query.group_by`.
- 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)
- )
- .. seealso::
- :meth:`_sql.Select.having` - v2 equivalent method.
- """
- for criterion in having:
- having_criteria = coercions.expect(
- roles.WhereHavingRole, criterion
- )
- self._having_criteria += (having_criteria,)
- return self
- def _set_op(self, expr_fn: Any, *q: Query[Any]) -> Self:
- list_of_queries = (self,) + q
- return self._from_selectable(expr_fn(*(list_of_queries)).subquery())
- def union(self, *q: Query[Any]) -> Self:
- """Produce a UNION of this Query against one or more queries.
- e.g.::
- q1 = sess.query(SomeClass).filter(SomeClass.foo == "bar")
- q2 = sess.query(SomeClass).filter(SomeClass.bar == "foo")
- q3 = q1.union(q2)
- The method accepts multiple Query objects so as to control
- the level of nesting. A series of ``union()`` calls such as::
- x.union(y).union(z).all()
- will nest on each ``union()``, and produces:
- .. sourcecode:: sql
- SELECT * FROM (SELECT * FROM (SELECT * FROM X UNION
- SELECT * FROM y) UNION SELECT * FROM Z)
- Whereas::
- x.union(y, z).all()
- produces:
- .. sourcecode:: sql
- SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y UNION
- SELECT * FROM Z)
- Note that many database backends do not allow ORDER BY to
- be rendered on a query called within UNION, EXCEPT, etc.
- To disable all ORDER BY clauses including those configured
- on mappers, issue ``query.order_by(None)`` - the resulting
- :class:`_query.Query` object will not render ORDER BY within
- its SELECT statement.
- .. seealso::
- :meth:`_sql.Select.union` - v2 equivalent method.
- """
- return self._set_op(expression.union, *q)
- def union_all(self, *q: Query[Any]) -> Self:
- """Produce a UNION ALL of this Query against one or more queries.
- Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
- that method for usage examples.
- .. seealso::
- :meth:`_sql.Select.union_all` - v2 equivalent method.
- """
- return self._set_op(expression.union_all, *q)
- def intersect(self, *q: Query[Any]) -> Self:
- """Produce an INTERSECT of this Query against one or more queries.
- Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
- that method for usage examples.
- .. seealso::
- :meth:`_sql.Select.intersect` - v2 equivalent method.
- """
- return self._set_op(expression.intersect, *q)
- def intersect_all(self, *q: Query[Any]) -> Self:
- """Produce an INTERSECT ALL of this Query against one or more queries.
- Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
- that method for usage examples.
- .. seealso::
- :meth:`_sql.Select.intersect_all` - v2 equivalent method.
- """
- return self._set_op(expression.intersect_all, *q)
- def except_(self, *q: Query[Any]) -> Self:
- """Produce an EXCEPT of this Query against one or more queries.
- Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
- that method for usage examples.
- .. seealso::
- :meth:`_sql.Select.except_` - v2 equivalent method.
- """
- return self._set_op(expression.except_, *q)
- def except_all(self, *q: Query[Any]) -> Self:
- """Produce an EXCEPT ALL of this Query against one or more queries.
- Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
- that method for usage examples.
- .. seealso::
- :meth:`_sql.Select.except_all` - v2 equivalent method.
- """
- return self._set_op(expression.except_all, *q)
- @_generative
- @_assertions(_no_statement_condition, _no_limit_offset)
- def join(
- self,
- target: _JoinTargetArgument,
- onclause: Optional[_OnClauseArgument] = None,
- *,
- isouter: bool = False,
- full: bool = False,
- ) -> Self:
- r"""Create a SQL JOIN against this :class:`_query.Query`
- object's criterion
- and apply generatively, returning the newly resulting
- :class:`_query.Query`.
- **Simple Relationship Joins**
- Consider a mapping between two classes ``User`` and ``Address``,
- with a relationship ``User.addresses`` representing a collection
- of ``Address`` objects associated with each ``User``. The most
- common usage of :meth:`_query.Query.join`
- is to create a JOIN along this
- relationship, using the ``User.addresses`` attribute as an indicator
- for how this should occur::
- 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
- SELECT user.id, user.name
- FROM user JOIN address ON user.id = address.user_id
- In the above example we refer to ``User.addresses`` as passed to
- :meth:`_query.Query.join` as the "on clause", that is, it indicates
- how the "ON" portion of the JOIN should be constructed.
- To construct a chain of joins, multiple :meth:`_query.Query.join`
- 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)
- )
- .. note:: as seen in the above example, **the order in which each
- call to the join() method occurs is important**. Query would not,
- for example, know how to join correctly if we were to specify
- ``User``, then ``Item``, then ``Order``, in our chain of joins; in
- such a case, depending on the arguments passed, it may raise an
- error that it doesn't know how to join, or it may produce invalid
- SQL in which case the database will raise an error. In correct
- practice, the
- :meth:`_query.Query.join` method is invoked in such a way that lines
- up with how we would want the JOIN clauses in SQL to be
- rendered, and each call should represent a clear link from what
- precedes it.
- **Joins to a Target Entity or Selectable**
- A second form of :meth:`_query.Query.join` allows any mapped entity or
- core selectable construct as a target. In this usage,
- :meth:`_query.Query.join` will attempt to create a JOIN along the
- natural foreign key relationship between two entities::
- q = session.query(User).join(Address)
- In the above calling form, :meth:`_query.Query.join` is called upon to
- create the "on clause" automatically for us. This calling form will
- ultimately raise an error if either there are no foreign keys between
- the two entities, or if there are multiple foreign key linkages between
- the target entity and the entity or entities already present on the
- left side such that creating a join requires more information. Note
- that when indicating a join to a target without any ON clause, ORM
- configured relationships are not taken into account.
- **Joins to a Target with an ON Clause**
- The third calling form allows both the target entity as well
- 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)
- The above form may also use a relationship-bound attribute as the
- ON clause as well::
- q = session.query(User).join(Address, User.addresses)
- The above syntax can be useful for the case where we wish
- to join to an alias of a particular target entity. If we wanted
- to join to ``Address`` twice, it could be achieved using two
- aliases set up using the :func:`~sqlalchemy.orm.aliased` function::
- 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")
- )
- The relationship-bound calling form can also specify a target entity
- using the :meth:`_orm.PropComparator.of_type` method; a query
- equivalent to the one above would be::
- 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")
- )
- **Augmenting Built-in ON Clauses**
- As a substitute for providing a full custom ON condition for an
- existing relationship, the :meth:`_orm.PropComparator.and_` function
- may be applied to a relationship attribute to augment additional
- criteria into the ON clause; the additional criteria will be combined
- with the default criteria using AND::
- q = session.query(User).join(
- User.addresses.and_(Address.email_address != "foo@bar.com")
- )
- .. versionadded:: 1.4
- **Joining to Tables and Subqueries**
- The target of a join may also be any table or SELECT statement,
- which may be related to a target entity or not. Use the
- 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()
- )
- 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()
- )
- address_subq = aliased(Address, subq)
- q = session.query(User).join(User.addresses.of_type(address_subq))
- **Controlling what to Join From**
- In cases where the left side of the current state of
- :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")
- )
- Which will produce SQL similar to:
- .. sourcecode:: sql
- SELECT address.* FROM user
- JOIN address ON user.id=address.user_id
- WHERE user.name = :name_1
- .. seealso::
- :meth:`_sql.Select.join` - v2 equivalent method.
- :param \*props: Incoming arguments for :meth:`_query.Query.join`,
- the props collection in modern use should be considered to be a one
- or two argument form, either as a single "target" entity or ORM
- attribute-bound relationship, or as a target entity plus an "on
- clause" which may be a SQL expression or ORM attribute-bound
- relationship.
- :param isouter=False: If True, the join used will be a left outer join,
- just as if the :meth:`_query.Query.outerjoin` method were called.
- :param full=False: render FULL OUTER JOIN; implies ``isouter``.
- """
- join_target = coercions.expect(
- roles.JoinTargetRole,
- target,
- apply_propagate_attrs=self,
- legacy=True,
- )
- if onclause is not None:
- onclause_element = coercions.expect(
- roles.OnClauseRole, onclause, legacy=True
- )
- else:
- onclause_element = None
- self._setup_joins += (
- (
- join_target,
- onclause_element,
- None,
- {
- "isouter": isouter,
- "full": full,
- },
- ),
- )
- self.__dict__.pop("_last_joined_entity", None)
- return self
- def outerjoin(
- self,
- target: _JoinTargetArgument,
- onclause: Optional[_OnClauseArgument] = None,
- *,
- full: bool = False,
- ) -> Self:
- """Create a left outer join against this ``Query`` object's criterion
- and apply generatively, returning the newly resulting ``Query``.
- Usage is the same as the ``join()`` method.
- .. seealso::
- :meth:`_sql.Select.outerjoin` - v2 equivalent method.
- """
- return self.join(target, onclause=onclause, isouter=True, full=full)
- @_generative
- @_assertions(_no_statement_condition)
- def reset_joinpoint(self) -> Self:
- """Return a new :class:`.Query`, where the "join point" has
- been reset back to the base FROM entities of the query.
- This method is usually used in conjunction with the
- ``aliased=True`` feature of the :meth:`~.Query.join`
- method. See the example in :meth:`~.Query.join` for how
- this is used.
- """
- self._last_joined_entity = None
- return self
- @_generative
- @_assertions(_no_clauseelement_condition)
- def select_from(self, *from_obj: _FromClauseArgument) -> Self:
- r"""Set the FROM clause of this :class:`.Query` explicitly.
- :meth:`.Query.select_from` is often used in conjunction with
- :meth:`.Query.join` in order to control which entity is selected
- from on the "left" side of the join.
- The entity or selectable object here effectively replaces the
- "left edge" of any calls to :meth:`~.Query.join`, when no
- joinpoint is otherwise established - usually, the default "join
- point" is the leftmost entity in the :class:`~.Query` object's
- list of entities to be selected.
- A typical example::
- q = (
- session.query(Address)
- .select_from(User)
- .join(User.addresses)
- .filter(User.name == "ed")
- )
- Which produces SQL equivalent to:
- .. sourcecode:: sql
- SELECT address.* FROM user
- JOIN address ON user.id=address.user_id
- WHERE user.name = :name_1
- :param \*from_obj: collection of one or more entities to apply
- to the FROM clause. Entities can be mapped classes,
- :class:`.AliasedClass` objects, :class:`.Mapper` objects
- as well as core :class:`.FromClause` elements like subqueries.
- .. seealso::
- :meth:`~.Query.join`
- :meth:`.Query.select_entity_from`
- :meth:`_sql.Select.select_from` - v2 equivalent method.
- """
- self._set_select_from(from_obj, False)
- return self
- def __getitem__(self, item: Any) -> Any:
- return orm_util._getitem(
- self,
- item,
- )
- @_generative
- @_assertions(_no_statement_condition)
- def slice(
- self,
- start: int,
- stop: int,
- ) -> Self:
- """Computes the "slice" of the :class:`_query.Query` represented by
- the given indices and returns the resulting :class:`_query.Query`.
- The start and stop indices behave like the argument to Python's
- built-in :func:`range` function. This method provides an
- alternative to using ``LIMIT``/``OFFSET`` to get a slice of the
- query.
- For example, ::
- session.query(User).order_by(User.id).slice(1, 3)
- renders as
- .. sourcecode:: sql
- SELECT users.id AS users_id,
- users.name AS users_name
- FROM users ORDER BY users.id
- LIMIT ? OFFSET ?
- (2, 1)
- .. seealso::
- :meth:`_query.Query.limit`
- :meth:`_query.Query.offset`
- :meth:`_sql.Select.slice` - v2 equivalent method.
- """
- self._limit_clause, self._offset_clause = sql_util._make_slice(
- self._limit_clause, self._offset_clause, start, stop
- )
- return self
- @_generative
- @_assertions(_no_statement_condition)
- def limit(self, limit: _LimitOffsetType) -> Self:
- """Apply a ``LIMIT`` to the query and return the newly resulting
- ``Query``.
- .. seealso::
- :meth:`_sql.Select.limit` - v2 equivalent method.
- """
- self._limit_clause = sql_util._offset_or_limit_clause(limit)
- return self
- @_generative
- @_assertions(_no_statement_condition)
- def offset(self, offset: _LimitOffsetType) -> Self:
- """Apply an ``OFFSET`` to the query and return the newly resulting
- ``Query``.
- .. seealso::
- :meth:`_sql.Select.offset` - v2 equivalent method.
- """
- self._offset_clause = sql_util._offset_or_limit_clause(offset)
- return self
- @_generative
- @_assertions(_no_statement_condition)
- def distinct(self, *expr: _ColumnExpressionArgument[Any]) -> Self:
- r"""Apply a ``DISTINCT`` to the query and return the newly resulting
- ``Query``.
- .. note::
- The ORM-level :meth:`.distinct` call includes logic that will
- automatically add columns from the ORDER BY of the query to the
- columns clause of the SELECT statement, to satisfy the common need
- of the database backend that ORDER BY columns be part of the SELECT
- list when DISTINCT is used. These columns *are not* added to the
- list of columns actually fetched by the :class:`_query.Query`,
- however,
- so would not affect results. The columns are passed through when
- using the :attr:`_query.Query.statement` accessor, however.
- .. deprecated:: 2.0 This logic is deprecated and will be removed
- in SQLAlchemy 2.0. See :ref:`migration_20_query_distinct`
- for a description of this use case in 2.0.
- .. seealso::
- :meth:`_sql.Select.distinct` - v2 equivalent method.
- :param \*expr: optional column expressions. When present,
- the PostgreSQL dialect will render a ``DISTINCT ON (<expressions>)``
- construct.
- .. deprecated:: 1.4 Using \*expr in other dialects is deprecated
- and will raise :class:`_exc.CompileError` in a future version.
- """
- if expr:
- self._distinct = True
- self._distinct_on = self._distinct_on + tuple(
- coercions.expect(roles.ByOfRole, e) for e in expr
- )
- else:
- self._distinct = True
- return self
- def all(self) -> List[_T]:
- """Return the results represented by this :class:`_query.Query`
- as a list.
- This results in an execution of the underlying SQL statement.
- .. warning:: The :class:`_query.Query` object,
- when asked to return either
- a sequence or iterator that consists of full ORM-mapped entities,
- will **deduplicate entries based on primary key**. See the FAQ for
- more details.
- .. seealso::
- :ref:`faq_query_deduplicating`
- .. seealso::
- :meth:`_engine.Result.all` - v2 comparable method.
- :meth:`_engine.Result.scalars` - v2 comparable method.
- """
- return self._iter().all() # type: ignore
- @_generative
- @_assertions(_no_clauseelement_condition)
- def from_statement(self, statement: ExecutableReturnsRows) -> Self:
- """Execute the given SELECT statement and return results.
- This method bypasses all internal statement compilation, and the
- statement is executed without modification.
- The statement is typically either a :func:`_expression.text`
- or :func:`_expression.select` construct, and should return the set
- of columns
- appropriate to the entity class represented by this
- :class:`_query.Query`.
- .. seealso::
- :meth:`_sql.Select.from_statement` - v2 comparable method.
- """
- statement = coercions.expect(
- roles.SelectStatementRole, statement, apply_propagate_attrs=self
- )
- self._statement = statement
- return self
- def first(self) -> Optional[_T]:
- """Return the first result of this ``Query`` or
- None if the result doesn't contain any row.
- first() applies a limit of one within the generated SQL, so that
- only one primary entity row is generated on the server side
- (note this may consist of multiple result rows if join-loaded
- collections are present).
- Calling :meth:`_query.Query.first`
- results in an execution of the underlying
- query.
- .. seealso::
- :meth:`_query.Query.one`
- :meth:`_query.Query.one_or_none`
- :meth:`_engine.Result.first` - v2 comparable method.
- :meth:`_engine.Result.scalars` - v2 comparable method.
- """
- # replicates limit(1) behavior
- if self._statement is not None:
- return self._iter().first() # type: ignore
- else:
- return self.limit(1)._iter().first() # type: ignore
- def one_or_none(self) -> Optional[_T]:
- """Return at most one result or raise an exception.
- Returns ``None`` 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:`_query.Query.one_or_none`
- results in an execution of the
- underlying query.
- .. seealso::
- :meth:`_query.Query.first`
- :meth:`_query.Query.one`
- :meth:`_engine.Result.one_or_none` - v2 comparable method.
- :meth:`_engine.Result.scalar_one_or_none` - v2 comparable method.
- """
- return self._iter().one_or_none() # type: ignore
- 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.
- Calling :meth:`.one` results in an execution of the underlying query.
- .. seealso::
- :meth:`_query.Query.first`
- :meth:`_query.Query.one_or_none`
- :meth:`_engine.Result.one` - v2 comparable method.
- :meth:`_engine.Result.scalar_one` - v2 comparable method.
- """
- return self._iter().one() # type: ignore
- 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`.
- >>> session.query(Item).scalar()
- <Item>
- >>> session.query(Item.id).scalar()
- 1
- >>> session.query(Item.id).filter(Item.id < 0).scalar()
- None
- >>> session.query(Item.id, Item.name).scalar()
- 1
- >>> session.query(func.count(Parent.id)).scalar()
- 20
- This results in an execution of the underlying query.
- .. seealso::
- :meth:`_engine.Result.scalar` - v2 comparable method.
- """
- # TODO: not sure why we can't use result.scalar() here
- try:
- ret = self.one()
- if not isinstance(ret, collections_abc.Sequence):
- return ret
- return ret[0]
- except sa_exc.NoResultFound:
- return None
- def __iter__(self) -> Iterator[_T]:
- result = self._iter()
- try:
- yield from result # type: ignore
- except GeneratorExit:
- # issue #8710 - direct iteration is not re-usable after
- # an iterable block is broken, so close the result
- result._soft_close()
- raise
- def _iter(self) -> Union[ScalarResult[_T], Result[_T]]:
- # new style execution.
- params = self._params
- statement = self._statement_20()
- result: Union[ScalarResult[_T], Result[_T]] = self.session.execute(
- statement,
- params,
- execution_options={"_sa_orm_load_options": self.load_options},
- )
- # legacy: automatically set scalars, unique
- if result._attributes.get("is_single_entity", False):
- result = cast("Result[_T]", result).scalars()
- if (
- result._attributes.get("filtered", False)
- and not self.load_options._yield_per
- ):
- result = result.unique()
- return result
- def __str__(self) -> str:
- statement = self._statement_20()
- try:
- bind = (
- self._get_bind_args(statement, self.session.get_bind)
- if self.session
- else None
- )
- except sa_exc.UnboundExecutionError:
- bind = None
- return str(statement.compile(bind))
- def _get_bind_args(self, statement: Any, fn: Any, **kw: Any) -> Any:
- return fn(clause=statement, **kw)
- @property
- def column_descriptions(self) -> List[ORMColumnDescription]:
- """Return metadata about the columns which would be
- returned by this :class:`_query.Query`.
- Format is a list of dictionaries::
- user_alias = aliased(User, name="user2")
- q = sess.query(User, User.id, user_alias)
- # this expression:
- q.column_descriptions
- # would return:
- [
- {
- "name": "User",
- "type": User,
- "aliased": False,
- "expr": User,
- "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,
- },
- ]
- .. seealso::
- This API is available using :term:`2.0 style` queries as well,
- documented at:
- * :ref:`queryguide_inspection`
- * :attr:`.Select.column_descriptions`
- """
- return _column_descriptions(self, legacy=True)
- @util.deprecated(
- "2.0",
- "The :meth:`_orm.Query.instances` method is deprecated and will "
- "be removed in a future release. "
- "Use the Select.from_statement() method or aliased() construct in "
- "conjunction with Session.execute() instead.",
- )
- def instances(
- self,
- result_proxy: CursorResult[Any],
- context: Optional[QueryContext] = None,
- ) -> Any:
- """Return an ORM result given a :class:`_engine.CursorResult` and
- :class:`.QueryContext`.
- """
- if context is None:
- util.warn_deprecated(
- "Using the Query.instances() method without a context "
- "is deprecated and will be disallowed in a future release. "
- "Please make use of :meth:`_query.Query.from_statement` "
- "for linking ORM results to arbitrary select constructs.",
- version="1.4",
- )
- compile_state = self._compile_state(for_statement=False)
- context = QueryContext(
- compile_state,
- compile_state.statement,
- compile_state.statement,
- self._params,
- self.session,
- self.load_options,
- )
- result = loading.instances(result_proxy, context)
- # legacy: automatically set scalars, unique
- if result._attributes.get("is_single_entity", False):
- result = result.scalars() # type: ignore
- if result._attributes.get("filtered", False):
- result = result.unique()
- # TODO: isn't this supposed to be a list?
- return result
- @util.became_legacy_20(
- ":meth:`_orm.Query.merge_result`",
- alternative="The method is superseded by the "
- ":func:`_orm.merge_frozen_result` function.",
- enable_warnings=False, # warnings occur via loading.merge_result
- )
- def merge_result(
- self,
- iterator: Union[
- FrozenResult[Any], Iterable[Sequence[Any]], Iterable[object]
- ],
- load: bool = True,
- ) -> Union[FrozenResult[Any], Iterable[Any]]:
- """Merge a result into this :class:`_query.Query` object's Session.
- Given an iterator returned by a :class:`_query.Query`
- of the same structure
- as this one, return an identical iterator of results, with all mapped
- instances merged into the session using :meth:`.Session.merge`. This
- is an optimized method which will merge all mapped instances,
- preserving the structure of the result rows and unmapped columns with
- less method overhead than that of calling :meth:`.Session.merge`
- explicitly for each value.
- The structure of the results is determined based on the column list of
- this :class:`_query.Query` - if these do not correspond,
- unchecked errors
- will occur.
- The 'load' argument is the same as that of :meth:`.Session.merge`.
- For an example of how :meth:`_query.Query.merge_result` is used, see
- the source code for the example :ref:`examples_caching`, where
- :meth:`_query.Query.merge_result` is used to efficiently restore state
- from a cache back into a target :class:`.Session`.
- """
- return loading.merge_result(self, iterator, load)
- def exists(self) -> Exists:
- """A convenience method that turns a query into an EXISTS subquery
- of the form EXISTS (SELECT 1 FROM ... WHERE ...).
- e.g.::
- q = session.query(User).filter(User.name == "fred")
- session.query(q.exists())
- Producing SQL similar to:
- .. sourcecode:: sql
- SELECT EXISTS (
- SELECT 1 FROM users WHERE users.name = :name_1
- ) AS anon_1
- The EXISTS construct is usually used in the WHERE clause::
- session.query(User.id).filter(q.exists()).scalar()
- Note that some databases such as SQL Server don't allow an
- EXISTS expression to be present in the columns clause of a
- SELECT. To select a simple boolean value based on the exists
- as a WHERE, use :func:`.literal`::
- from sqlalchemy import literal
- session.query(literal(True)).filter(q.exists()).scalar()
- .. seealso::
- :meth:`_sql.Select.exists` - v2 comparable method.
- """
- # .add_columns() for the case that we are a query().select_from(X),
- # so that ".statement" can be produced (#2995) but also without
- # omitting the FROM clause from a query(X) (#2818);
- # .with_only_columns() after we have a core select() so that
- # we get just "SELECT 1" without any entities.
- inner = (
- self.enable_eagerloads(False)
- .add_columns(sql.literal_column("1"))
- .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
- ._get_select_statement_only()
- .with_only_columns(1)
- )
- ezero = self._entity_from_pre_ent_zero()
- if ezero is not None:
- inner = inner.select_from(ezero)
- return sql.exists(inner)
- def count(self) -> int:
- 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
- SELECT count(1) AS count_1 FROM (
- SELECT <rest of query follows...>
- ) AS anon_1
- The above SQL returns a single row, which is the aggregate value
- of the count function; the :meth:`_query.Query.count`
- method then returns
- that single integer value.
- .. warning::
- It is important to note that the value returned by
- count() is **not the same as the number of ORM objects that this
- Query would return from a method such as the .all() method**.
- The :class:`_query.Query` object,
- when asked to return full entities,
- will **deduplicate entries based on primary key**, meaning if the
- same primary key value would appear in the results more than once,
- only one object of that primary key would be present. This does
- not apply to a query that is against individual columns.
- .. seealso::
- :ref:`faq_query_deduplicating`
- For fine grained control over specific columns to count, to skip the
- usage of a subquery or otherwise control of the FROM clause, or to use
- other aggregate functions, use :attr:`~sqlalchemy.sql.expression.func`
- expressions in conjunction with :meth:`~.Session.query`, i.e.::
- from sqlalchemy import func
- # count User records, without
- # using a subquery.
- session.query(func.count(User.id))
- # return count of user "id" grouped
- # by "name"
- session.query(func.count(User.id)).group_by(User.name)
- from sqlalchemy import distinct
- # count distinct "name" values
- session.query(func.count(distinct(User.name)))
- .. seealso::
- :ref:`migration_20_query_usage`
- """
- col = sql.func.count(sql.literal_column("*"))
- return ( # type: ignore
- self._legacy_from_self(col).enable_eagerloads(False).scalar()
- )
- def delete(
- self,
- synchronize_session: SynchronizeSessionArgument = "auto",
- delete_args: Optional[Dict[Any, Any]] = None,
- ) -> int:
- r"""Perform a DELETE with an arbitrary WHERE clause.
- Deletes rows matched by this query from the database.
- E.g.::
- sess.query(User).filter(User.age == 25).delete(synchronize_session=False)
- sess.query(User).filter(User.age == 25).delete(
- synchronize_session="evaluate"
- )
- .. warning::
- See the section :ref:`orm_expression_update_delete` for important
- caveats and warnings, including limitations when using bulk UPDATE
- and DELETE with mapper inheritance configurations.
- :param synchronize_session: chooses the strategy to update the
- attributes on objects in the session. See the section
- :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.
- .. seealso::
- :ref:`orm_expression_update_delete`
- """ # noqa: E501
- bulk_del = BulkDelete(self, delete_args)
- if self.dispatch.before_compile_delete:
- for fn in self.dispatch.before_compile_delete:
- new_query = fn(bulk_del.query, bulk_del)
- if new_query is not None:
- bulk_del.query = new_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_,
- self._params,
- execution_options=self._execution_options.union(
- {"synchronize_session": synchronize_session}
- ),
- )
- bulk_del.result = result # type: ignore
- self.session.dispatch.after_bulk_delete(bulk_del)
- result.close()
- return result.rowcount
- def update(
- self,
- values: Dict[_DMLColumnArgument, Any],
- synchronize_session: SynchronizeSessionArgument = "auto",
- update_args: Optional[Dict[Any, Any]] = None,
- ) -> int:
- r"""Perform an UPDATE with an arbitrary WHERE clause.
- Updates rows matched by this query in the database.
- 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(
- {"age": User.age - 10}, synchronize_session="evaluate"
- )
- .. warning::
- See the section :ref:`orm_expression_update_delete` for important
- caveats and warnings, including limitations when using arbitrary
- UPDATE and DELETE with mapper inheritance configurations.
- :param values: a dictionary with attributes names, or alternatively
- mapped attributes or SQL expressions, as keys, and literal
- values or sql expressions as values. If :ref:`parameter-ordered
- mode <tutorial_parameter_ordered_updates>` is desired, the values can
- be passed as a list of 2-tuples; this requires that the
- :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order`
- flag is passed to the :paramref:`.Query.update.update_args` dictionary
- as well.
- :param synchronize_session: chooses the strategy to update the
- attributes on objects in the session. See the section
- :ref:`orm_expression_update_delete` for a discussion of these
- 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
- as ``mysql_limit``, as well as other special arguments such as
- :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order`.
- :return: the count of rows matched as returned by the database's
- "row count" feature.
- .. seealso::
- :ref:`orm_expression_update_delete`
- """
- update_args = update_args or {}
- bulk_ud = BulkUpdate(self, values, update_args)
- if self.dispatch.before_compile_update:
- for fn in self.dispatch.before_compile_update:
- new_query = fn(bulk_ud.query, bulk_ud)
- if new_query is not None:
- bulk_ud.query = new_query
- self = bulk_ud.query
- upd = sql.update(*self._raw_columns) # type: ignore
- ppo = update_args.pop("preserve_parameter_order", False)
- if ppo:
- upd = upd.ordered_values(*values) # type: ignore
- else:
- upd = upd.values(values)
- if update_args:
- upd = upd.with_dialect_options(**update_args)
- upd._where_criteria = self._where_criteria
- result: CursorResult[Any] = self.session.execute(
- upd,
- self._params,
- execution_options=self._execution_options.union(
- {"synchronize_session": synchronize_session}
- ),
- )
- bulk_ud.result = result # type: ignore
- self.session.dispatch.after_bulk_update(bulk_ud)
- result.close()
- return result.rowcount
- def _compile_state(
- self, for_statement: bool = False, **kw: Any
- ) -> ORMCompileState:
- """Create an out-of-compiler ORMCompileState object.
- The ORMCompileState object is normally created directly as a result
- of the SQLCompiler.process() method being handed a Select()
- or FromStatement() object that uses the "orm" plugin. This method
- provides a means of creating this ORMCompileState object directly
- without using the compiler.
- This method is used only for deprecated cases, which include
- the .from_self() method for a Query that has multiple levels
- of .from_self() in use, as well as the instances() method. It is
- also used within the test suite to generate ORMCompileState objects
- for test purposes.
- """
- stmt = self._statement_20(for_statement=for_statement, **kw)
- assert for_statement == stmt._compile_options._for_statement
- # this chooses between ORMFromStatementCompileState and
- # ORMSelectCompileState. We could also base this on
- # query._statement is not None as we have the ORM Query here
- # however this is the more general path.
- compile_state_cls = cast(
- ORMCompileState,
- ORMCompileState._get_plugin_class_for_plugin(stmt, "orm"),
- )
- return compile_state_cls._create_orm_context(
- stmt, toplevel=True, compiler=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,
- )
- return context
- class AliasOption(interfaces.LoaderOption):
- inherit_cache = False
- @util.deprecated(
- "1.4",
- "The :class:`.AliasOption` object is not necessary "
- "for entities to be matched up to a query that is established "
- "via :meth:`.Query.from_statement` and now does nothing.",
- )
- def __init__(self, alias: Union[Alias, Subquery]):
- r"""Return a :class:`.MapperOption` that will indicate to the
- :class:`_query.Query`
- that the main table has been aliased.
- """
- def process_compile_state(self, compile_state: ORMCompileState) -> None:
- pass
- class BulkUD:
- """State used for the orm.Query version of update() / delete().
- This object is now specific to Query only.
- """
- def __init__(self, query: Query[Any]):
- self.query = query.enable_eagerloads(False)
- self._validate_query_state()
- self.mapper = self.query._entity_from_pre_ent_zero()
- def _validate_query_state(self) -> None:
- for attr, methname, notset, op in (
- ("_limit_clause", "limit()", None, operator.is_),
- ("_offset_clause", "offset()", None, operator.is_),
- ("_order_by_clauses", "order_by()", (), operator.eq),
- ("_group_by_clauses", "group_by()", (), operator.eq),
- ("_distinct", "distinct()", False, operator.is_),
- (
- "_from_obj",
- "join(), outerjoin(), select_from(), or from_self()",
- (),
- operator.eq,
- ),
- (
- "_setup_joins",
- "join(), outerjoin(), select_from(), or from_self()",
- (),
- operator.eq,
- ),
- ):
- if not op(getattr(self.query, attr), notset):
- raise sa_exc.InvalidRequestError(
- "Can't call Query.update() or Query.delete() "
- "when %s has been called" % (methname,)
- )
- @property
- def session(self) -> Session:
- return self.query.session
- class BulkUpdate(BulkUD):
- """BulkUD which handles UPDATEs."""
- def __init__(
- self,
- query: Query[Any],
- values: Dict[_DMLColumnArgument, Any],
- update_kwargs: Optional[Dict[Any, Any]],
- ):
- super().__init__(query)
- self.values = values
- self.update_kwargs = update_kwargs
- 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:
- def tuples(self) -> Query[_TP]: # type: ignore
- ...
|