210
def _adapt_all_clauses(self):
211
self._orm_only_adapt = False
238
213
def _adapt_clause(self, clause, as_filter, orm_only):
214
"""Adapt incoming clauses to transformations which have been applied
215
within this query."""
219
# do we adapt all expression elements or only those
220
# tagged as 'ORM' constructs ?
221
orm_only = getattr(self, '_orm_only_adapt', orm_only)
240
223
if as_filter and self._filter_aliases:
241
224
for fa in self._filter_aliases._visitor_iterator:
242
adapters.append(fa.replace)
244
231
if self._from_obj_alias:
245
adapters.append(self._from_obj_alias.replace)
232
# for the "from obj" alias, apply extra rule to the
233
# 'ORM only' check, if this query were generated from a
234
# subquery of itself, i.e. _from_selectable(), apply adaption
235
# to all SQL constructs.
238
getattr(self, '_orm_only_from_obj_alias', orm_only),
239
self._from_obj_alias.replace
247
243
if self._polymorphic_adapters:
248
adapters.append(self.__adapt_polymorphic_element)
246
orm_only, self.__adapt_polymorphic_element
253
if getattr(self, '_disable_orm_filtering', not orm_only):
254
return visitors.replacement_traverse(
256
{'column_collections':False},
257
self.__replace_element(adapters)
260
return visitors.replacement_traverse(
262
{'column_collections':False},
263
self.__replace_orm_element(adapters)
254
for _orm_only, adapter in adapters:
255
# if 'orm only', look for ORM annotations
256
# in the element before adapting.
257
if not _orm_only or \
258
'_orm_adapt' in elem._annotations or \
259
"parententity" in elem._annotations:
265
return visitors.replacement_traverse(
266
271
def _entity_zero(self):
267
272
return self._entities[0]
631
634
self._execution_options['stream_results'] = True
633
636
def get(self, ident):
634
"""Return an instance of the object based on the
635
given identifier, or ``None`` if not found.
637
The ``ident`` argument is a scalar or tuple of
638
primary key column values
639
in the order of the mapper's "primary key" setting, which
640
defaults to the list of primary key columns for the
641
mapped :class:`.Table`.
643
:meth:`get` returns only a single mapped instance, or
644
``None``. It is not intended to return rows or scalar
645
column values, therefore the :class:`.Query` must be
646
constructed only against a single mapper or mapped class,
647
not a SQL expression or multiple entities.
648
Other usages raise an error, or in the case of a single
649
column a deprecation warning is raised as of 0.6.8.
637
"""Return an instance based on the given primary key identifier,
638
or ``None`` if not found.
642
my_user = session.query(User).get(5)
644
some_object = session.query(VersionedFoo).get((5, 10))
646
:meth:`~.Query.get` is special in that it provides direct
647
access to the identity map of the owning :class:`.Session`.
648
If the given primary key identifier is present
649
in the local identity map, the object is returned
650
directly from this collection and no SQL is emitted,
651
unless the object has been marked fully expired.
653
a SELECT is performed in order to locate the object.
655
:meth:`~.Query.get` also will perform a check if
656
the object is present in the identity map and
657
marked as expired - a SELECT
658
is emitted to refresh the object as well as to
659
ensure that the row is still present.
660
If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised.
662
:meth:`~.Query.get` is only used to return a single
663
mapped instance, not multiple instances or
664
individual column constructs, and strictly
665
on a single primary key value. The originating
666
:class:`.Query` must be constructed in this way,
667
i.e. against a single mapped entity,
668
with no additional filtering criterion. Loading
669
options via :meth:`~.Query.options` may be applied
670
however, and will be used if the object is not
673
A lazy-loading, many-to-one attribute configured
674
by :func:`.relationship`, using a simple
675
foreign-key-to-primary-key criterion, will also use an
676
operation equivalent to :meth:`~.Query.get` in order to retrieve
677
the target value from the local identity map
678
before querying the database. See :ref:`loading_toplevel`
679
for further details on relationship loading.
681
:param ident: A scalar or tuple value representing
682
the primary key. For a composite primary key,
683
the order of identifiers corresponds in most cases
684
to that of the mapped :class:`.Table` object's
685
primary key columns. For a :func:`.mapper` that
686
was given the ``primary key`` argument during
687
construction, the order of identifiers corresponds
688
to the elements present in this collection.
690
:return: The object instance, or ``None``.
653
694
# convert composite types to individual args
654
695
if hasattr(ident, '__composite_values__'):
655
696
ident = ident.__composite_values__()
657
key = self._only_full_mapper_zero("get"
658
).identity_key_from_primary_key(ident)
659
return self._get(key, ident)
698
ident = util.to_list(ident)
700
mapper = self._only_full_mapper_zero("get")
702
if len(ident) != len(mapper.primary_key):
703
raise sa_exc.InvalidRequestError(
704
"Incorrect number of values in identifier to formulate "
705
"primary key for query.get(); primary key columns are %s" %
706
','.join("'%s'" % c for c in mapper.primary_key))
708
key = mapper.identity_key_from_primary_key(ident)
710
if not self._populate_existing and \
711
not mapper.always_refresh and \
712
self._lockmode is None:
714
instance = self._get_from_identity(self.session, key, False)
715
if instance is not None:
716
# reject calls for id in identity map but class
718
if not issubclass(instance.__class__, mapper.class_):
722
return self._load_on_ident(key)
662
725
def correlate(self, *args):
1163
1253
* a class-mapped attribute, i.e. Houses.rooms. This will create a
1164
1254
join from "Houses" table to that of the "rooms" relationship.
1166
* a 2-tuple containing a target class or selectable, and an "ON"
1167
clause. The ON clause can be the property name/ attribute like
1168
above, or a SQL expression.
1256
A two-element form of \*props may also be passed. In this form,
1257
the first element is a target class or selectable, the second
1258
is a string property name, class-mapped attribute, or clause
1259
construct representing an "ON" clause. This supersedes the
1260
previous "tuple" calling form - multiple join() calls should
1261
be used for multiple (target, onclause) pairs.
1209
1301
same table. Consider usage of the aliased(SomeClass) construct as
1210
1302
a more explicit approach to this.
1212
from_joinpoint - when joins are specified using string property
1213
names, locate the property from the mapper found in the most
1214
recent previous join() call, instead of from the root entity.
1304
from_joinpoint - the given join conditions will attempt
1305
to join from the right endpoint of the most recent join(),
1306
instead of from the query's root entity. I.e. any chain
1314
join(b, from_joinpoint=True).\\
1315
join(c, from_joinpoint=True)
1317
See also :ref:`ormtutorial_joins` in the ORM tutorial.
1217
1320
aliased, from_joinpoint = kwargs.pop('aliased', False),\
1218
1321
kwargs.pop('from_joinpoint', False)
1251
1353
if not from_joinpoint:
1252
1354
self._reset_joinpoint()
1254
if len(keys) >= 2 and \
1255
isinstance(keys[1], expression.ClauseElement) and \
1256
not isinstance(keys[1], expression.FromClause):
1257
raise sa_exc.ArgumentError(
1258
"You appear to be passing a clause expression as the second "
1259
"argument to query.join(). Did you mean to use the form "
1260
"query.join((target, onclause))? Note the tuple.")
1356
if len(keys) == 2 and \
1357
isinstance(keys[0], (expression.FromClause,
1358
type, AliasedClass)) and \
1359
isinstance(keys[1], (basestring, expression.ClauseElement,
1360
interfaces.PropComparator)):
1361
# detect 2-arg form of join and
1362
# convert to a tuple.
1262
1365
for arg1 in util.to_list(keys):
1263
1366
if isinstance(arg1, tuple):
1367
# "tuple" form of join, multiple
1368
# tuples are accepted as well. The simpler
1369
# "2-arg" form is preferred. May deprecate
1370
# the "tuple" usage.
1264
1371
arg1, arg2 = arg1
1720
1838
self.session._autoflush()
1721
1839
return self._execute_and_instances(context)
1841
def _connection_from_session(self, **kw):
1842
conn = self.session.connection(
1844
if self._execution_options:
1845
conn = conn.execution_options(**self._execution_options)
1723
1848
def _execute_and_instances(self, querycontext):
1724
result = self.session.execute(
1725
querycontext.statement, params=self._params,
1726
mapper=self._mapper_zero_or_none())
1849
conn = self._connection_from_session(
1850
mapper = self._mapper_zero_or_none(),
1851
clause = querycontext.statement,
1852
close_with_result=True)
1854
result = conn.execute(querycontext.statement, self._params)
1727
1855
return self.instances(result, querycontext)
1730
1858
def column_descriptions(self):
1731
1859
"""Return metadata about the columns which would be
1732
returned by this :class:`Query`.
1860
returned by this :class:`.Query`.
1734
1862
Format is a list of dictionaries::
1906
2036
session.autoflush = autoflush
1909
def _get(self, key=None, ident=None, refresh_state=None, lockmode=None,
1910
only_load_props=None, passive=None):
2039
def _get_from_identity(cls, session, key, passive):
2040
"""Look up the given key in the given session's identity map,
2041
check the object for expired state if found.
2044
instance = session.identity_map.get(key)
2047
state = attributes.instance_state(instance)
2049
# expired - ensure it still exists
2051
if passive is attributes.PASSIVE_NO_FETCH:
2052
# TODO: no coverage here
2053
return attributes.PASSIVE_NO_RESULT
2054
elif passive is attributes.PASSIVE_NO_FETCH_RELATED:
2055
# this mode is used within a flush and the instance's
2056
# expired state will be checked soon enough, if necessary
2060
except orm_exc.ObjectDeletedError:
2061
session._remove_newly_deleted(state)
2067
def _load_on_ident(self, key, refresh_state=None, lockmode=None,
2068
only_load_props=None):
2069
"""Load the given identity key from the database."""
1911
2071
lockmode = lockmode or self._lockmode
1913
mapper = self._mapper_zero()
1914
if not self._populate_existing and \
1915
not refresh_state and \
1916
not mapper.always_refresh and \
1918
instance = self.session.identity_map.get(key)
1920
# item present in identity map with a different class
1921
if not issubclass(instance.__class__, mapper.class_):
1924
state = attributes.instance_state(instance)
1926
# expired - ensure it still exists
1928
if passive is attributes.PASSIVE_NO_FETCH:
1929
return attributes.PASSIVE_NO_RESULT
1932
except orm_exc.ObjectDeletedError:
1933
self.session._remove_newly_deleted(state)
1936
elif passive is attributes.PASSIVE_NO_FETCH:
1937
return attributes.PASSIVE_NO_RESULT
1943
ident = util.to_list(ident)
1945
2078
if refresh_state is None:
1946
2079
q = self._clone()
2011
2140
def count(self):
2012
2141
"""Return a count of rows this Query would return.
2014
For simple entity queries, count() issues
2015
a SELECT COUNT, and will specifically count the primary
2016
key column of the first entity only. If the query uses
2017
LIMIT, OFFSET, or DISTINCT, count() will wrap the statement
2018
generated by this Query in a subquery, from which a SELECT COUNT
2019
is issued, so that the contract of "how many rows
2020
would be returned?" is honored.
2022
For queries that request specific columns or expressions,
2023
count() again makes no assumptions about those expressions
2024
and will wrap everything in a subquery. Therefore,
2025
``Query.count()`` is usually not what you want in this case.
2026
To count specific columns, often in conjunction with
2027
GROUP BY, use ``func.count()`` as an individual column expression
2028
instead of ``Query.count()``. See the ORM tutorial
2143
This generates the SQL for this Query as follows::
2145
SELECT count(1) AS count_1 FROM (
2146
SELECT <rest of query follows...>
2149
Note the above scheme is newly refined in 0.7
2152
For fine grained control over specific columns
2153
to count, to skip the usage of a subquery or
2154
otherwise control of the FROM clause,
2155
or to use other aggregate functions,
2156
use :attr:`.func` expressions in conjunction
2157
with :meth:`~.Session.query`, i.e.::
2159
from sqlalchemy import func
2161
# count User records, without
2163
session.query(func.count(User.id))
2165
# return count of user "id" grouped
2167
session.query(func.count(User.id)).\\
2170
from sqlalchemy import distinct
2172
# count distinct "name" values
2173
session.query(func.count(distinct(User.name)))
2032
should_nest = [self._should_nest_selectable]
2034
if isinstance(ent, _MapperEntity):
2035
return ent.mapper.primary_key
2037
should_nest[0] = True
2040
return self._col_aggregate(sql.literal_column('1'), sql.func.count,
2041
nested_cols=chain(*[ent_cols(ent) for ent in self._entities]),
2042
should_nest = should_nest[0]
2045
def _col_aggregate(self, col, func, nested_cols=None, should_nest=False):
2046
context = QueryContext(self)
2048
for entity in self._entities:
2049
entity.setup_context(self, context)
2051
if context.from_clause:
2052
from_obj = list(context.from_clause)
2054
from_obj = context.froms
2056
if self._enable_single_crit:
2057
self._adjust_for_single_inheritance(context)
2059
whereclause = context.whereclause
2065
nested_cols = list(nested_cols)
2066
s = sql.select(nested_cols, whereclause,
2067
from_obj=from_obj, use_labels=True,
2068
**self._select_args)
2071
[func(s.corresponding_column(col) or col)]).select_from(s)
2073
s = sql.select([func(col)], whereclause, from_obj=from_obj,
2074
**self._select_args)
2076
if self._autoflush and not self._populate_existing:
2077
self.session._autoflush()
2078
return self.session.scalar(s, params=self._params,
2079
mapper=self._mapper_zero())
2176
col = sql.func.count(sql.literal_column('*'))
2177
return self.from_self(col).scalar()
2081
2179
def delete(self, synchronize_session='evaluate'):
2082
2180
"""Perform a bulk delete query.
2183
2269
session.identity_map.iteritems()
2184
2270
if issubclass(cls, target_cls) and
2185
2271
eval_condition(obj)]
2273
elif synchronize_session == 'fetch':
2274
#TODO: use RETURNING when available
2275
select_stmt = context.statement.with_only_columns(
2276
primary_table.primary_key)
2277
matched_rows = session.execute(
2279
params=self._params).fetchall()
2281
delete_stmt = sql.delete(primary_table, context.whereclause)
2283
result = session.execute(delete_stmt, params=self._params)
2285
if synchronize_session == 'evaluate':
2186
2286
for obj in objs_to_expunge:
2187
2287
session._remove_newly_deleted(attributes.instance_state(obj))
2188
2288
elif synchronize_session == 'fetch':
2298
2400
"Could not evaluate current criteria in Python. "
2299
2401
"Specify 'fetch' or False for the "
2300
2402
"synchronize_session parameter.")
2302
update_stmt = sql.update(primary_table, context.whereclause, values)
2304
if synchronize_session == 'fetch':
2305
select_stmt = context.statement.with_only_columns(
2306
primary_table.primary_key)
2307
matched_rows = session.execute(
2309
params=self._params).fetchall()
2312
session._autoflush()
2313
result = session.execute(update_stmt, params=self._params)
2315
if synchronize_session == 'evaluate':
2316
2403
target_cls = self._mapper_zero().class_
2404
matched_objects = []
2318
2405
for (cls, pk),obj in session.identity_map.iteritems():
2319
2406
evaluated_keys = value_evaluators.keys()
2321
2408
if issubclass(cls, target_cls) and eval_condition(obj):
2322
state, dict_ = attributes.instance_state(obj),\
2323
attributes.instance_dict(obj)
2325
# only evaluate unmodified attributes
2326
to_evaluate = state.unmodified.intersection(
2328
for key in to_evaluate:
2329
dict_[key] = value_evaluators[key](obj)
2331
state.commit(dict_, list(to_evaluate))
2333
# expire attributes with pending changes
2334
# (there was no autoflush, so they are overwritten)
2335
state.expire_attributes(dict_,
2336
set(evaluated_keys).
2337
difference(to_evaluate))
2409
matched_objects.append(obj)
2411
elif synchronize_session == 'fetch':
2412
select_stmt = context.statement.with_only_columns(
2413
primary_table.primary_key)
2414
matched_rows = session.execute(
2416
params=self._params).fetchall()
2418
update_stmt = sql.update(primary_table, context.whereclause, values)
2420
result = session.execute(update_stmt, params=self._params)
2422
if synchronize_session == 'evaluate':
2423
target_cls = self._mapper_zero().class_
2425
for obj in matched_objects:
2426
state, dict_ = attributes.instance_state(obj),\
2427
attributes.instance_dict(obj)
2429
# only evaluate unmodified attributes
2430
to_evaluate = state.unmodified.intersection(
2432
for key in to_evaluate:
2433
dict_[key] = value_evaluators[key](obj)
2435
state.commit(dict_, list(to_evaluate))
2437
# expire attributes with pending changes
2438
# (there was no autoflush, so they are overwritten)
2439
state.expire_attributes(dict_,
2440
set(evaluated_keys).
2441
difference(to_evaluate))
2339
2443
elif synchronize_session == 'fetch':
2340
2444
target_mapper = self._mapper_zero()
2568
2666
def setup_entity(self, entity, mapper, adapter,
2569
2667
from_obj, is_aliased_class, with_polymorphic):
2570
2668
self.mapper = mapper
2571
self.extension = self.mapper.extension
2572
2669
self.adapter = adapter
2573
2670
self.selectable = from_obj
2574
2671
self._with_polymorphic = with_polymorphic
2575
2672
self._polymorphic_discriminator = None
2576
2673
self.is_aliased_class = is_aliased_class
2577
2674
if is_aliased_class:
2578
self.path_entity = self.entity = self.entity_zero = entity
2579
self._label_name = self.entity._sa_label_name
2675
self.path_entity = self.entity_zero = entity
2676
self._path = (entity,)
2677
self._label_name = self.entity_zero._sa_label_name
2678
self._reduced_path = (self.path_entity, )
2581
2680
self.path_entity = mapper
2582
self.entity = self.entity_zero = mapper
2681
self._path = (mapper,)
2682
self._reduced_path = (mapper.base_mapper, )
2683
self.entity_zero = mapper
2583
2684
self._label_name = self.mapper.class_.__name__
2585
2687
def set_with_polymorphic(self, query, cls_or_mappers,
2586
2688
selectable, discriminator):
2587
2689
if cls_or_mappers is None:
2780
2890
def type(self):
2781
2891
return self.column.type
2893
def filter_fn(self, item):
2783
2896
def adapt_to_selectable(self, query, sel):
2784
2897
c = _ColumnEntity(query, sel.corresponding_column(self.column))
2898
c._label_name = self._label_name
2785
2899
c.entity_zero = self.entity_zero
2786
2900
c.entities = self.entities
2788
2902
def setup_entity(self, entity, mapper, adapter, from_obj,
2789
2903
is_aliased_class, with_polymorphic):
2790
self.selectable = from_obj
2904
if 'selectable' not in self.__dict__:
2905
self.selectable = from_obj
2791
2906
self.froms.add(from_obj)
2793
2908
def corresponds_to(self, entity):