PageRenderTime 35ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/skink/lib/sqlalchemy/orm/query.py

http://github.com/heynemann/skink
Python | 2229 lines | 2041 code | 84 blank | 104 comment | 117 complexity | 64a55e88461865cebd7b7979695f18be MD5 | raw file
  1. # orm/query.py
  2. # Copyright (C) 2005, 2006, 2007, 2008, 2009 Michael Bayer mike_mp@zzzcomputing.com
  3. #
  4. # This module is part of SQLAlchemy and is released under
  5. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  6. """The Query class and support.
  7. Defines the :class:`~sqlalchemy.orm.query.Query` class, the central construct used by
  8. the ORM to construct database queries.
  9. The ``Query`` class should not be confused with the
  10. :class:`~sqlalchemy.sql.expression.Select` class, which defines database SELECT
  11. operations at the SQL (non-ORM) level. ``Query`` differs from ``Select`` in
  12. that it returns ORM-mapped objects and interacts with an ORM session, whereas
  13. the ``Select`` construct interacts directly with the database to return
  14. iterable result sets.
  15. """
  16. from itertools import chain
  17. from operator import itemgetter
  18. from sqlalchemy import sql, util, log, schema
  19. from sqlalchemy import exc as sa_exc
  20. from sqlalchemy.orm import exc as orm_exc
  21. from sqlalchemy.sql import util as sql_util
  22. from sqlalchemy.sql import expression, visitors, operators
  23. from sqlalchemy.orm import (
  24. attributes, interfaces, mapper, object_mapper, evaluator,
  25. )
  26. from sqlalchemy.orm.util import (
  27. AliasedClass, ORMAdapter, _entity_descriptor, _entity_info,
  28. _is_aliased_class, _is_mapped_class, _orm_columns, _orm_selectable,
  29. join as orm_join,
  30. )
  31. __all__ = ['Query', 'QueryContext', 'aliased']
  32. aliased = AliasedClass
  33. def _generative(*assertions):
  34. """Mark a method as generative."""
  35. @util.decorator
  36. def generate(fn, *args, **kw):
  37. self = args[0]._clone()
  38. for assertion in assertions:
  39. assertion(self, fn.func_name)
  40. fn(self, *args[1:], **kw)
  41. return self
  42. return generate
  43. class Query(object):
  44. """ORM-level SQL construction object."""
  45. _enable_eagerloads = True
  46. _enable_assertions = True
  47. _with_labels = False
  48. _criterion = None
  49. _yield_per = None
  50. _lockmode = None
  51. _order_by = False
  52. _group_by = False
  53. _having = None
  54. _distinct = False
  55. _offset = None
  56. _limit = None
  57. _statement = None
  58. _joinpoint = None
  59. _correlate = frozenset()
  60. _populate_existing = False
  61. _version_check = False
  62. _autoflush = True
  63. _current_path = ()
  64. _only_load_props = None
  65. _refresh_state = None
  66. _from_obj = ()
  67. _filter_aliases = None
  68. _from_obj_alias = None
  69. _currenttables = frozenset()
  70. def __init__(self, entities, session=None):
  71. self.session = session
  72. self._with_options = []
  73. self._params = {}
  74. self._attributes = {}
  75. self._polymorphic_adapters = {}
  76. self._set_entities(entities)
  77. def _set_entities(self, entities, entity_wrapper=None):
  78. if entity_wrapper is None:
  79. entity_wrapper = _QueryEntity
  80. self._entities = []
  81. for ent in util.to_list(entities):
  82. entity_wrapper(self, ent)
  83. self._setup_aliasizers(self._entities)
  84. def _setup_aliasizers(self, entities):
  85. if hasattr(self, '_mapper_adapter_map'):
  86. # usually safe to share a single map, but copying to prevent
  87. # subtle leaks if end-user is reusing base query with arbitrary
  88. # number of aliased() objects
  89. self._mapper_adapter_map = d = self._mapper_adapter_map.copy()
  90. else:
  91. self._mapper_adapter_map = d = {}
  92. for ent in entities:
  93. for entity in ent.entities:
  94. if entity not in d:
  95. mapper, selectable, is_aliased_class = _entity_info(entity)
  96. if not is_aliased_class and mapper.with_polymorphic:
  97. with_polymorphic = mapper._with_polymorphic_mappers
  98. self.__mapper_loads_polymorphically_with(mapper,
  99. sql_util.ColumnAdapter(selectable, mapper._equivalent_columns))
  100. adapter = None
  101. elif is_aliased_class:
  102. adapter = sql_util.ColumnAdapter(selectable, mapper._equivalent_columns)
  103. with_polymorphic = None
  104. else:
  105. with_polymorphic = adapter = None
  106. d[entity] = (mapper, adapter, selectable, is_aliased_class, with_polymorphic)
  107. ent.setup_entity(entity, *d[entity])
  108. def __mapper_loads_polymorphically_with(self, mapper, adapter):
  109. for m2 in mapper._with_polymorphic_mappers:
  110. self._polymorphic_adapters[m2] = adapter
  111. for m in m2.iterate_to_root():
  112. self._polymorphic_adapters[m.mapped_table] = self._polymorphic_adapters[m.local_table] = adapter
  113. def _set_select_from(self, from_obj):
  114. if isinstance(from_obj, expression._SelectBaseMixin):
  115. from_obj = from_obj.alias()
  116. self._from_obj = (from_obj,)
  117. equivs = self.__all_equivs()
  118. if isinstance(from_obj, expression.Alias):
  119. self._from_obj_alias = sql_util.ColumnAdapter(from_obj, equivs)
  120. def _get_polymorphic_adapter(self, entity, selectable):
  121. self.__mapper_loads_polymorphically_with(entity.mapper,
  122. sql_util.ColumnAdapter(selectable, entity.mapper._equivalent_columns))
  123. def _reset_polymorphic_adapter(self, mapper):
  124. for m2 in mapper._with_polymorphic_mappers:
  125. self._polymorphic_adapters.pop(m2, None)
  126. for m in m2.iterate_to_root():
  127. self._polymorphic_adapters.pop(m.mapped_table, None)
  128. self._polymorphic_adapters.pop(m.local_table, None)
  129. def _reset_joinpoint(self):
  130. self._joinpoint = None
  131. self._filter_aliases = None
  132. def __adapt_polymorphic_element(self, element):
  133. if isinstance(element, expression.FromClause):
  134. search = element
  135. elif hasattr(element, 'table'):
  136. search = element.table
  137. else:
  138. search = None
  139. if search:
  140. alias = self._polymorphic_adapters.get(search, None)
  141. if alias:
  142. return alias.adapt_clause(element)
  143. def __replace_element(self, adapters):
  144. def replace(elem):
  145. if '_halt_adapt' in elem._annotations:
  146. return elem
  147. for adapter in adapters:
  148. e = adapter(elem)
  149. if e:
  150. return e
  151. return replace
  152. def __replace_orm_element(self, adapters):
  153. def replace(elem):
  154. if '_halt_adapt' in elem._annotations:
  155. return elem
  156. if "_orm_adapt" in elem._annotations or "parententity" in elem._annotations:
  157. for adapter in adapters:
  158. e = adapter(elem)
  159. if e:
  160. return e
  161. return replace
  162. @_generative()
  163. def _adapt_all_clauses(self):
  164. self._disable_orm_filtering = True
  165. def _adapt_clause(self, clause, as_filter, orm_only):
  166. adapters = []
  167. if as_filter and self._filter_aliases:
  168. adapters.append(self._filter_aliases.replace)
  169. if self._from_obj_alias:
  170. adapters.append(self._from_obj_alias.replace)
  171. if self._polymorphic_adapters:
  172. adapters.append(self.__adapt_polymorphic_element)
  173. if not adapters:
  174. return clause
  175. if getattr(self, '_disable_orm_filtering', not orm_only):
  176. return visitors.replacement_traverse(
  177. clause,
  178. {'column_collections':False},
  179. self.__replace_element(adapters)
  180. )
  181. else:
  182. return visitors.replacement_traverse(
  183. clause,
  184. {'column_collections':False},
  185. self.__replace_orm_element(adapters)
  186. )
  187. def _entity_zero(self):
  188. return self._entities[0]
  189. def _mapper_zero(self):
  190. return self._entity_zero().entity_zero
  191. def _extension_zero(self):
  192. ent = self._entity_zero()
  193. return getattr(ent, 'extension', ent.mapper.extension)
  194. @property
  195. def _mapper_entities(self):
  196. # TODO: this is wrong, its hardcoded to "priamry entity" when
  197. # for the case of __all_equivs() it should not be
  198. # the name of this accessor is wrong too
  199. for ent in self._entities:
  200. if hasattr(ent, 'primary_entity'):
  201. yield ent
  202. def _joinpoint_zero(self):
  203. return self._joinpoint or self._entity_zero().entity_zero
  204. def _mapper_zero_or_none(self):
  205. if not getattr(self._entities[0], 'primary_entity', False):
  206. return None
  207. return self._entities[0].mapper
  208. def _only_mapper_zero(self, rationale=None):
  209. if len(self._entities) > 1:
  210. raise sa_exc.InvalidRequestError(
  211. rationale or "This operation requires a Query against a single mapper."
  212. )
  213. return self._mapper_zero()
  214. def _only_entity_zero(self, rationale=None):
  215. if len(self._entities) > 1:
  216. raise sa_exc.InvalidRequestError(
  217. rationale or "This operation requires a Query against a single mapper."
  218. )
  219. return self._entity_zero()
  220. def _generate_mapper_zero(self):
  221. if not getattr(self._entities[0], 'primary_entity', False):
  222. raise sa_exc.InvalidRequestError("No primary mapper set up for this Query.")
  223. entity = self._entities[0]._clone()
  224. self._entities = [entity] + self._entities[1:]
  225. return entity
  226. def __all_equivs(self):
  227. equivs = {}
  228. for ent in self._mapper_entities:
  229. equivs.update(ent.mapper._equivalent_columns)
  230. return equivs
  231. def _no_criterion_condition(self, meth):
  232. if not self._enable_assertions:
  233. return
  234. if self._criterion or self._statement or self._from_obj or \
  235. self._limit is not None or self._offset is not None or \
  236. self._group_by:
  237. raise sa_exc.InvalidRequestError("Query.%s() being called on a Query with existing criterion. " % meth)
  238. self._from_obj = ()
  239. self._statement = self._criterion = None
  240. self._order_by = self._group_by = self._distinct = False
  241. def _no_clauseelement_condition(self, meth):
  242. if not self._enable_assertions:
  243. return
  244. if self._order_by:
  245. raise sa_exc.InvalidRequestError("Query.%s() being called on a Query with existing criterion. " % meth)
  246. self._no_criterion_condition(meth)
  247. def _no_statement_condition(self, meth):
  248. if not self._enable_assertions:
  249. return
  250. if self._statement:
  251. raise sa_exc.InvalidRequestError(
  252. ("Query.%s() being called on a Query with an existing full "
  253. "statement - can't apply criterion.") % meth)
  254. def _no_limit_offset(self, meth):
  255. if not self._enable_assertions:
  256. return
  257. if self._limit is not None or self._offset is not None:
  258. raise sa_exc.InvalidRequestError(
  259. "Query.%s() being called on a Query which already has LIMIT or OFFSET applied. "
  260. "To modify the row-limited results of a Query, call from_self() first. "
  261. "Otherwise, call %s() before limit() or offset() are applied." % (meth, meth)
  262. )
  263. def _no_select_modifiers(self, meth):
  264. if not self._enable_assertions:
  265. return
  266. for attr, methname, notset in (
  267. ('_limit', 'limit()', None),
  268. ('_offset', 'offset()', None),
  269. ('_order_by', 'order_by()', False),
  270. ('_group_by', 'group_by()', False),
  271. ('_distinct', 'distinct()', False),
  272. ):
  273. if getattr(self, attr) is not notset:
  274. raise sa_exc.InvalidRequestError(
  275. "Can't call Query.%s() when %s has been called" % (meth, methname)
  276. )
  277. def _get_options(self, populate_existing=None,
  278. version_check=None,
  279. only_load_props=None,
  280. refresh_state=None):
  281. if populate_existing:
  282. self._populate_existing = populate_existing
  283. if version_check:
  284. self._version_check = version_check
  285. if refresh_state:
  286. self._refresh_state = refresh_state
  287. if only_load_props:
  288. self._only_load_props = set(only_load_props)
  289. return self
  290. def _clone(self):
  291. cls = self.__class__
  292. q = cls.__new__(cls)
  293. q.__dict__ = self.__dict__.copy()
  294. return q
  295. @property
  296. def statement(self):
  297. """The full SELECT statement represented by this Query."""
  298. return self._compile_context(labels=self._with_labels).\
  299. statement._annotate({'_halt_adapt': True})
  300. def subquery(self):
  301. """return the full SELECT statement represented by this Query, embedded within an Alias.
  302. Eager JOIN generation within the query is disabled.
  303. """
  304. return self.enable_eagerloads(False).statement.alias()
  305. def __clause_element__(self):
  306. return self.enable_eagerloads(False).statement
  307. @_generative()
  308. def enable_eagerloads(self, value):
  309. """Control whether or not eager joins are rendered.
  310. When set to False, the returned Query will not render
  311. eager joins regardless of eagerload() options
  312. or mapper-level lazy=False configurations.
  313. This is used primarily when nesting the Query's
  314. statement into a subquery or other
  315. selectable.
  316. """
  317. self._enable_eagerloads = value
  318. @_generative()
  319. def with_labels(self):
  320. """Apply column labels to the return value of Query.statement.
  321. Indicates that this Query's `statement` accessor should return
  322. a SELECT statement that applies labels to all columns in the
  323. form <tablename>_<columnname>; this is commonly used to
  324. disambiguate columns from multiple tables which have the same
  325. name.
  326. When the `Query` actually issues SQL to load rows, it always
  327. uses column labeling.
  328. """
  329. self._with_labels = True
  330. @_generative()
  331. def enable_assertions(self, value):
  332. """Control whether assertions are generated.
  333. When set to False, the returned Query will
  334. not assert its state before certain operations,
  335. including that LIMIT/OFFSET has not been applied
  336. when filter() is called, no criterion exists
  337. when get() is called, and no "from_statement()"
  338. exists when filter()/order_by()/group_by() etc.
  339. is called. This more permissive mode is used by
  340. custom Query subclasses to specify criterion or
  341. other modifiers outside of the usual usage patterns.
  342. Care should be taken to ensure that the usage
  343. pattern is even possible. A statement applied
  344. by from_statement() will override any criterion
  345. set by filter() or order_by(), for example.
  346. """
  347. self._enable_assertions = value
  348. @property
  349. def whereclause(self):
  350. """The WHERE criterion for this Query."""
  351. return self._criterion
  352. @_generative()
  353. def _with_current_path(self, path):
  354. """indicate that this query applies to objects loaded within a certain path.
  355. Used by deferred loaders (see strategies.py) which transfer query
  356. options from an originating query to a newly generated query intended
  357. for the deferred load.
  358. """
  359. self._current_path = path
  360. @_generative(_no_clauseelement_condition)
  361. def with_polymorphic(self, cls_or_mappers, selectable=None, discriminator=None):
  362. """Load columns for descendant mappers of this Query's mapper.
  363. Using this method will ensure that each descendant mapper's
  364. tables are included in the FROM clause, and will allow filter()
  365. criterion to be used against those tables. The resulting
  366. instances will also have those columns already loaded so that
  367. no "post fetch" of those columns will be required.
  368. :param cls_or_mappers: a single class or mapper, or list of class/mappers,
  369. which inherit from this Query's mapper. Alternatively, it
  370. may also be the string ``'*'``, in which case all descending
  371. mappers will be added to the FROM clause.
  372. :param selectable: a table or select() statement that will
  373. be used in place of the generated FROM clause. This argument
  374. is required if any of the desired mappers use concrete table
  375. inheritance, since SQLAlchemy currently cannot generate UNIONs
  376. among tables automatically. If used, the ``selectable``
  377. argument must represent the full set of tables and columns mapped
  378. by every desired mapper. Otherwise, the unaccounted mapped columns
  379. will result in their table being appended directly to the FROM
  380. clause which will usually lead to incorrect results.
  381. :param discriminator: a column to be used as the "discriminator"
  382. column for the given selectable. If not given, the polymorphic_on
  383. attribute of the mapper will be used, if any. This is useful
  384. for mappers that don't have polymorphic loading behavior by default,
  385. such as concrete table mappers.
  386. """
  387. entity = self._generate_mapper_zero()
  388. entity.set_with_polymorphic(self, cls_or_mappers, selectable=selectable, discriminator=discriminator)
  389. @_generative()
  390. def yield_per(self, count):
  391. """Yield only ``count`` rows at a time.
  392. WARNING: use this method with caution; if the same instance is present
  393. in more than one batch of rows, end-user changes to attributes will be
  394. overwritten.
  395. In particular, it's usually impossible to use this setting with
  396. eagerly loaded collections (i.e. any lazy=False) since those
  397. collections will be cleared for a new load when encountered in a
  398. subsequent result batch.
  399. """
  400. self._yield_per = count
  401. def get(self, ident):
  402. """Return an instance of the object based on the given identifier, or None if not found.
  403. The `ident` argument is a scalar or tuple of primary key column values
  404. in the order of the table def's primary key columns.
  405. """
  406. # convert composite types to individual args
  407. if hasattr(ident, '__composite_values__'):
  408. ident = ident.__composite_values__()
  409. key = self._only_mapper_zero(
  410. "get() can only be used against a single mapped class."
  411. ).identity_key_from_primary_key(ident)
  412. return self._get(key, ident)
  413. @classmethod
  414. @util.deprecated('Deprecated. Use sqlalchemy.orm.with_parent '
  415. 'in conjunction with filter().')
  416. def query_from_parent(cls, instance, property, **kwargs):
  417. """Return a new Query with criterion corresponding to a parent instance.
  418. Return a newly constructed Query object, with criterion corresponding
  419. to a relationship to the given parent instance.
  420. instance
  421. a persistent or detached instance which is related to class
  422. represented by this query.
  423. property
  424. string name of the property which relates this query's class to the
  425. instance.
  426. \**kwargs
  427. all extra keyword arguments are propagated to the constructor of
  428. Query.
  429. """
  430. mapper = object_mapper(instance)
  431. prop = mapper.get_property(property, resolve_synonyms=True)
  432. target = prop.mapper
  433. criterion = prop.compare(operators.eq, instance, value_is_parent=True)
  434. return Query(target, **kwargs).filter(criterion)
  435. @_generative()
  436. def correlate(self, *args):
  437. self._correlate = self._correlate.union(_orm_selectable(s) for s in args)
  438. @_generative()
  439. def autoflush(self, setting):
  440. """Return a Query with a specific 'autoflush' setting.
  441. Note that a Session with autoflush=False will
  442. not autoflush, even if this flag is set to True at the
  443. Query level. Therefore this flag is usually used only
  444. to disable autoflush for a specific Query.
  445. """
  446. self._autoflush = setting
  447. @_generative()
  448. def populate_existing(self):
  449. """Return a Query that will refresh all instances loaded.
  450. This includes all entities accessed from the database, including
  451. secondary entities, eagerly-loaded collection items.
  452. All changes present on entities which are already present in the
  453. session will be reset and the entities will all be marked "clean".
  454. An alternative to populate_existing() is to expire the Session
  455. fully using session.expire_all().
  456. """
  457. self._populate_existing = True
  458. def with_parent(self, instance, property=None):
  459. """Add a join criterion corresponding to a relationship to the given
  460. parent instance.
  461. instance
  462. a persistent or detached instance which is related to class
  463. represented by this query.
  464. property
  465. string name of the property which relates this query's class to the
  466. instance. if None, the method will attempt to find a suitable
  467. property.
  468. Currently, this method only works with immediate parent relationships,
  469. but in the future may be enhanced to work across a chain of parent
  470. mappers.
  471. """
  472. from sqlalchemy.orm import properties
  473. mapper = object_mapper(instance)
  474. if property is None:
  475. for prop in mapper.iterate_properties:
  476. if isinstance(prop, properties.PropertyLoader) and prop.mapper is self._mapper_zero():
  477. break
  478. else:
  479. raise sa_exc.InvalidRequestError(
  480. "Could not locate a property which relates instances "
  481. "of class '%s' to instances of class '%s'" %
  482. (self._mapper_zero().class_.__name__, instance.__class__.__name__)
  483. )
  484. else:
  485. prop = mapper.get_property(property, resolve_synonyms=True)
  486. return self.filter(prop.compare(operators.eq, instance, value_is_parent=True))
  487. @_generative()
  488. def add_entity(self, entity, alias=None):
  489. """add a mapped entity to the list of result columns to be returned."""
  490. if alias:
  491. entity = aliased(entity, alias)
  492. self._entities = list(self._entities)
  493. m = _MapperEntity(self, entity)
  494. self._setup_aliasizers([m])
  495. def from_self(self, *entities):
  496. """return a Query that selects from this Query's SELECT statement.
  497. \*entities - optional list of entities which will replace
  498. those being selected.
  499. """
  500. fromclause = self.with_labels().enable_eagerloads(False).statement.correlate(None)
  501. q = self._from_selectable(fromclause)
  502. if entities:
  503. q._set_entities(entities)
  504. return q
  505. _from_self = from_self
  506. @_generative()
  507. def _from_selectable(self, fromclause):
  508. self._statement = self._criterion = None
  509. self._order_by = self._group_by = self._distinct = False
  510. self._limit = self._offset = None
  511. self._set_select_from(fromclause)
  512. def values(self, *columns):
  513. """Return an iterator yielding result tuples corresponding to the given list of columns"""
  514. if not columns:
  515. return iter(())
  516. q = self._clone()
  517. q._set_entities(columns, entity_wrapper=_ColumnEntity)
  518. if not q._yield_per:
  519. q._yield_per = 10
  520. return iter(q)
  521. _values = values
  522. def value(self, column):
  523. """Return a scalar result corresponding to the given column expression."""
  524. try:
  525. return self.values(column).next()[0]
  526. except StopIteration:
  527. return None
  528. @_generative()
  529. def add_column(self, column):
  530. """Add a SQL ColumnElement to the list of result columns to be returned."""
  531. self._entities = list(self._entities)
  532. l = len(self._entities)
  533. _ColumnEntity(self, column)
  534. # _ColumnEntity may add many entities if the
  535. # given arg is a FROM clause
  536. self._setup_aliasizers(self._entities[l:])
  537. def options(self, *args):
  538. """Return a new Query object, applying the given list of
  539. MapperOptions.
  540. """
  541. return self._options(False, *args)
  542. def _conditional_options(self, *args):
  543. return self._options(True, *args)
  544. @_generative()
  545. def _options(self, conditional, *args):
  546. # most MapperOptions write to the '_attributes' dictionary,
  547. # so copy that as well
  548. self._attributes = self._attributes.copy()
  549. opts = [o for o in util.flatten_iterator(args)]
  550. self._with_options = self._with_options + opts
  551. if conditional:
  552. for opt in opts:
  553. opt.process_query_conditionally(self)
  554. else:
  555. for opt in opts:
  556. opt.process_query(self)
  557. @_generative()
  558. def with_lockmode(self, mode):
  559. """Return a new Query object with the specified locking mode."""
  560. self._lockmode = mode
  561. @_generative()
  562. def params(self, *args, **kwargs):
  563. """add values for bind parameters which may have been specified in filter().
  564. parameters may be specified using \**kwargs, or optionally a single dictionary
  565. as the first positional argument. The reason for both is that \**kwargs is
  566. convenient, however some parameter dictionaries contain unicode keys in which case
  567. \**kwargs cannot be used.
  568. """
  569. if len(args) == 1:
  570. kwargs.update(args[0])
  571. elif len(args) > 0:
  572. raise sa_exc.ArgumentError("params() takes zero or one positional argument, which is a dictionary.")
  573. self._params = self._params.copy()
  574. self._params.update(kwargs)
  575. @_generative(_no_statement_condition, _no_limit_offset)
  576. def filter(self, criterion):
  577. """apply the given filtering criterion to the query and return the newly resulting ``Query``
  578. the criterion is any sql.ClauseElement applicable to the WHERE clause of a select.
  579. """
  580. if isinstance(criterion, basestring):
  581. criterion = sql.text(criterion)
  582. if criterion is not None and not isinstance(criterion, sql.ClauseElement):
  583. raise sa_exc.ArgumentError("filter() argument must be of type sqlalchemy.sql.ClauseElement or string")
  584. criterion = self._adapt_clause(criterion, True, True)
  585. if self._criterion is not None:
  586. self._criterion = self._criterion & criterion
  587. else:
  588. self._criterion = criterion
  589. def filter_by(self, **kwargs):
  590. """apply the given filtering criterion to the query and return the newly resulting ``Query``."""
  591. clauses = [_entity_descriptor(self._joinpoint_zero(), key)[0] == value
  592. for key, value in kwargs.iteritems()]
  593. return self.filter(sql.and_(*clauses))
  594. @_generative(_no_statement_condition, _no_limit_offset)
  595. @util.accepts_a_list_as_starargs(list_deprecation='pending')
  596. def order_by(self, *criterion):
  597. """apply one or more ORDER BY criterion to the query and return the newly resulting ``Query``"""
  598. if len(criterion) == 1 and criterion[0] is None:
  599. self._order_by = None
  600. else:
  601. criterion = [self._adapt_clause(expression._literal_as_text(o), True, True) for o in criterion]
  602. if self._order_by is False or self._order_by is None:
  603. self._order_by = criterion
  604. else:
  605. self._order_by = self._order_by + criterion
  606. @_generative(_no_statement_condition, _no_limit_offset)
  607. @util.accepts_a_list_as_starargs(list_deprecation='pending')
  608. def group_by(self, *criterion):
  609. """apply one or more GROUP BY criterion to the query and return the newly resulting ``Query``"""
  610. criterion = list(chain(*[_orm_columns(c) for c in criterion]))
  611. criterion = [self._adapt_clause(expression._literal_as_text(o), True, True) for o in criterion]
  612. if self._group_by is False:
  613. self._group_by = criterion
  614. else:
  615. self._group_by = self._group_by + criterion
  616. @_generative(_no_statement_condition, _no_limit_offset)
  617. def having(self, criterion):
  618. """apply a HAVING criterion to the query and return the newly resulting ``Query``."""
  619. if isinstance(criterion, basestring):
  620. criterion = sql.text(criterion)
  621. if criterion is not None and not isinstance(criterion, sql.ClauseElement):
  622. raise sa_exc.ArgumentError("having() argument must be of type sqlalchemy.sql.ClauseElement or string")
  623. criterion = self._adapt_clause(criterion, True, True)
  624. if self._having is not None:
  625. self._having = self._having & criterion
  626. else:
  627. self._having = criterion
  628. def union(self, *q):
  629. """Produce a UNION of this Query against one or more queries.
  630. e.g.::
  631. q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar')
  632. q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo')
  633. q3 = q1.union(q2)
  634. The method accepts multiple Query objects so as to control
  635. the level of nesting. A series of ``union()`` calls such as::
  636. x.union(y).union(z).all()
  637. will nest on each ``union()``, and produces::
  638. SELECT * FROM (SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y) UNION SELECT * FROM Z)
  639. Whereas::
  640. x.union(y, z).all()
  641. produces::
  642. SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y UNION SELECT * FROM Z)
  643. """
  644. return self._from_selectable(
  645. expression.union(*([self]+ list(q))))
  646. def union_all(self, *q):
  647. """Produce a UNION ALL of this Query against one or more queries.
  648. Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See that
  649. method for usage examples.
  650. """
  651. return self._from_selectable(
  652. expression.union_all(*([self]+ list(q)))
  653. )
  654. def intersect(self, *q):
  655. """Produce an INTERSECT of this Query against one or more queries.
  656. Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See that
  657. method for usage examples.
  658. """
  659. return self._from_selectable(
  660. expression.intersect(*([self]+ list(q)))
  661. )
  662. def intersect_all(self, *q):
  663. """Produce an INTERSECT ALL of this Query against one or more queries.
  664. Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See that
  665. method for usage examples.
  666. """
  667. return self._from_selectable(
  668. expression.intersect_all(*([self]+ list(q)))
  669. )
  670. def except_(self, *q):
  671. """Produce an EXCEPT of this Query against one or more queries.
  672. Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See that
  673. method for usage examples.
  674. """
  675. return self._from_selectable(
  676. expression.except_(*([self]+ list(q)))
  677. )
  678. def except_all(self, *q):
  679. """Produce an EXCEPT ALL of this Query against one or more queries.
  680. Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See that
  681. method for usage examples.
  682. """
  683. return self._from_selectable(
  684. expression.except_all(*([self]+ list(q)))
  685. )
  686. @util.accepts_a_list_as_starargs(list_deprecation='pending')
  687. def join(self, *props, **kwargs):
  688. """Create a join against this ``Query`` object's criterion
  689. and apply generatively, returning the newly resulting ``Query``.
  690. Each element in \*props may be:
  691. * a string property name, i.e. "rooms". This will join along the
  692. relation of the same name from this Query's "primary" mapper, if
  693. one is present.
  694. * a class-mapped attribute, i.e. Houses.rooms. This will create a
  695. join from "Houses" table to that of the "rooms" relation.
  696. * a 2-tuple containing a target class or selectable, and an "ON"
  697. clause. The ON clause can be the property name/ attribute like
  698. above, or a SQL expression.
  699. e.g.::
  700. # join along string attribute names
  701. session.query(Company).join('employees')
  702. session.query(Company).join('employees', 'tasks')
  703. # join the Person entity to an alias of itself,
  704. # along the "friends" relation
  705. PAlias = aliased(Person)
  706. session.query(Person).join((Palias, Person.friends))
  707. # join from Houses to the "rooms" attribute on the
  708. # "Colonials" subclass of Houses, then join to the
  709. # "closets" relation on Room
  710. session.query(Houses).join(Colonials.rooms, Room.closets)
  711. # join from Company entities to the "employees" collection,
  712. # using "people JOIN engineers" as the target. Then join
  713. # to the "computers" collection on the Engineer entity.
  714. session.query(Company).join((people.join(engineers), 'employees'), Engineer.computers)
  715. # join from Articles to Keywords, using the "keywords" attribute.
  716. # assume this is a many-to-many relation.
  717. session.query(Article).join(Article.keywords)
  718. # same thing, but spelled out entirely explicitly
  719. # including the association table.
  720. session.query(Article).join(
  721. (article_keywords, Articles.id==article_keywords.c.article_id),
  722. (Keyword, Keyword.id==article_keywords.c.keyword_id)
  723. )
  724. \**kwargs include:
  725. aliased - when joining, create anonymous aliases of each table. This is
  726. used for self-referential joins or multiple joins to the same table.
  727. Consider usage of the aliased(SomeClass) construct as a more explicit
  728. approach to this.
  729. from_joinpoint - when joins are specified using string property names,
  730. locate the property from the mapper found in the most recent previous
  731. join() call, instead of from the root entity.
  732. """
  733. aliased, from_joinpoint = kwargs.pop('aliased', False), kwargs.pop('from_joinpoint', False)
  734. if kwargs:
  735. raise TypeError("unknown arguments: %s" % ','.join(kwargs.iterkeys()))
  736. return self._join(props, outerjoin=False, create_aliases=aliased, from_joinpoint=from_joinpoint)
  737. @util.accepts_a_list_as_starargs(list_deprecation='pending')
  738. def outerjoin(self, *props, **kwargs):
  739. """Create a left outer join against this ``Query`` object's criterion
  740. and apply generatively, retunring the newly resulting ``Query``.
  741. Usage is the same as the ``join()`` method.
  742. """
  743. aliased, from_joinpoint = kwargs.pop('aliased', False), kwargs.pop('from_joinpoint', False)
  744. if kwargs:
  745. raise TypeError("unknown arguments: %s" % ','.join(kwargs.iterkeys()))
  746. return self._join(props, outerjoin=True, create_aliases=aliased, from_joinpoint=from_joinpoint)
  747. @_generative(_no_statement_condition, _no_limit_offset)
  748. def _join(self, keys, outerjoin, create_aliases, from_joinpoint):
  749. # copy collections that may mutate so they do not affect
  750. # the copied-from query.
  751. self._currenttables = set(self._currenttables)
  752. self._polymorphic_adapters = self._polymorphic_adapters.copy()
  753. # start from the beginning unless from_joinpoint is set.
  754. if not from_joinpoint:
  755. self._reset_joinpoint()
  756. clause = replace_clause_index = None
  757. # after the method completes,
  758. # the query's joinpoint will be set to this.
  759. right_entity = None
  760. for arg1 in util.to_list(keys):
  761. aliased_entity = False
  762. alias_criterion = False
  763. left_entity = right_entity
  764. prop = of_type = right_entity = right_mapper = None
  765. # distinguish between tuples, scalar args
  766. if isinstance(arg1, tuple):
  767. arg1, arg2 = arg1
  768. else:
  769. arg2 = None
  770. # determine onclause/right_entity. there
  771. # is a little bit of legacy behavior still at work here
  772. # which means they might be in either order. may possibly
  773. # lock this down to (right_entity, onclause) in 0.6.
  774. if isinstance(arg2, (interfaces.PropComparator, basestring)):
  775. onclause = arg2
  776. right_entity = arg1
  777. elif isinstance(arg1, (interfaces.PropComparator, basestring)):
  778. onclause = arg1
  779. right_entity = arg2
  780. else:
  781. onclause = arg2
  782. right_entity = arg1
  783. # extract info from the onclause argument, determine
  784. # left_entity and right_entity.
  785. if isinstance(onclause, interfaces.PropComparator):
  786. of_type = getattr(onclause, '_of_type', None)
  787. prop = onclause.property
  788. descriptor = onclause
  789. if not left_entity:
  790. left_entity = onclause.parententity
  791. if of_type:
  792. right_mapper = of_type
  793. else:
  794. right_mapper = prop.mapper
  795. if not right_entity:
  796. right_entity = right_mapper
  797. elif isinstance(onclause, basestring):
  798. if not left_entity:
  799. left_entity = self._joinpoint_zero()
  800. descriptor, prop = _entity_descriptor(left_entity, onclause)
  801. right_mapper = prop.mapper
  802. if not right_entity:
  803. right_entity = right_mapper
  804. elif not left_entity:
  805. left_entity = self._joinpoint_zero()
  806. if not clause and self._from_obj:
  807. mp, left_selectable, is_aliased_class = _entity_info(left_entity)
  808. replace_clause_index, clause = sql_util.find_join_source(self._from_obj, left_selectable)
  809. if not clause:
  810. clause = left_selectable
  811. if not clause and left_entity:
  812. for ent in self._entities:
  813. if ent.corresponds_to(left_entity):
  814. clause = ent.selectable
  815. break
  816. # TODO:
  817. # this provides one kind of "backwards join"
  818. # tested in test/orm/query.py.
  819. # removal of this has been considered, but maybe not
  820. # see [ticket:1445]
  821. if not clause:
  822. if isinstance(onclause, interfaces.PropComparator):
  823. clause = onclause.__clause_element__()
  824. if not clause:
  825. raise sa_exc.InvalidRequestError("Could not find a FROM clause to join from")
  826. # if we have a MapperProperty and the onclause is not already
  827. # an instrumented descriptor. this catches of_type()
  828. # PropComparators and string-based on clauses.
  829. if prop and not isinstance(onclause, attributes.QueryableAttribute):
  830. onclause = prop
  831. # start looking at the right side of the join
  832. mp, right_selectable, is_aliased_class = _entity_info(right_entity)
  833. if mp is not None and right_mapper is not None and not mp.common_parent(right_mapper):
  834. raise sa_exc.InvalidRequestError(
  835. "Join target %s does not correspond to the right side of join condition %s" % (right_entity, onclause)
  836. )
  837. if not right_mapper and mp:
  838. right_mapper = mp
  839. # determine if we need to wrap the right hand side in an alias.
  840. # this occurs based on the create_aliases flag, or if the target
  841. # is a selectable, Join, or polymorphically-loading mapper
  842. if right_mapper and not is_aliased_class:
  843. if right_entity is right_selectable:
  844. if not right_selectable.is_derived_from(right_mapper.mapped_table):
  845. raise sa_exc.InvalidRequestError(
  846. "Selectable '%s' is not derived from '%s'" %
  847. (right_selectable.description, right_mapper.mapped_table.description))
  848. if not isinstance(right_selectable, expression.Alias):
  849. right_selectable = right_selectable.alias()
  850. right_entity = aliased(right_mapper, right_selectable)
  851. alias_criterion = True
  852. elif create_aliases:
  853. right_entity = aliased(right_mapper)
  854. alias_criterion = True
  855. elif right_mapper.with_polymorphic or isinstance(right_mapper.mapped_table, expression.Join):
  856. right_entity = aliased(right_mapper)
  857. alias_criterion = True
  858. aliased_entity = True
  859. elif prop:
  860. # for joins across plain relation()s, try not to specify the
  861. # same joins twice. the _currenttables collection tracks
  862. # what plain mapped tables we've joined to already.
  863. if prop.table in self._currenttables:
  864. if prop.secondary is not None and prop.secondary not in self._currenttables:
  865. # TODO: this check is not strong enough for different paths to the same endpoint which
  866. # does not use secondary tables
  867. raise sa_exc.InvalidRequestError("Can't join to property '%s'; a path to this "
  868. "table along a different secondary table already "
  869. "exists. Use the `alias=True` argument to `join()`." % descriptor)
  870. continue
  871. if prop.secondary:
  872. self._currenttables.add(prop.secondary)
  873. self._currenttables.add(prop.table)
  874. if of_type:
  875. right_entity = of_type
  876. else:
  877. right_entity = prop.mapper
  878. # create adapters to the right side, if we've created aliases
  879. if alias_criterion:
  880. right_adapter = ORMAdapter(right_entity,
  881. equivalents=right_mapper._equivalent_columns, chain_to=self._filter_aliases)
  882. # if the onclause is a ClauseElement, adapt it with our right
  883. # adapter, then with our query-wide adaptation if any.
  884. if isinstance(onclause, expression.ClauseElement):
  885. if alias_criterion:
  886. onclause = right_adapter.traverse(onclause)
  887. onclause = self._adapt_clause(onclause, False, True)
  888. # determine if we want _ORMJoin to alias the onclause
  889. # to the given left side. This is used if we're joining against a
  890. # select_from() selectable, from_self() call, or the onclause
  891. # has been resolved into a MapperProperty. Otherwise we assume
  892. # the onclause itself contains more specific information on how to
  893. # construct the onclause.
  894. join_to_left = not is_aliased_class or \
  895. onclause is prop or \
  896. self._from_obj_alias and clause is self._from_obj[0]
  897. # create the join
  898. clause = orm_join(clause, right_entity, onclause, isouter=outerjoin, join_to_left=join_to_left)
  899. # set up state for the query as a whole
  900. if alias_criterion:
  901. # adapt filter() calls based on our right side adaptation
  902. self._filter_aliases = right_adapter
  903. # if a polymorphic entity was aliased, establish that
  904. # so that MapperEntity/ColumnEntity can pick up on it
  905. # and adapt when it renders columns and fetches them from results
  906. if aliased_entity:
  907. self.__mapper_loads_polymorphically_with(
  908. right_mapper,
  909. ORMAdapter(right_entity, equivalents=right_mapper._equivalent_columns)
  910. )
  911. if replace_clause_index is not None:
  912. l = list(self._from_obj)
  913. l[replace_clause_index] = clause
  914. self._from_obj = tuple(l)
  915. else:
  916. self._from_obj = self._from_obj + (clause,)
  917. # future joins with from_joinpoint=True join from our established right_entity.
  918. self._joinpoint = right_entity
  919. @_generative(_no_statement_condition)
  920. def reset_joinpoint(self):
  921. """return a new Query reset the 'joinpoint' of this Query reset
  922. back to the starting mapper. Subsequent generative calls will
  923. be constructed from the new joinpoint.
  924. Note that each call to join() or outerjoin() also starts from
  925. the root.
  926. """
  927. self._reset_joinpoint()
  928. @_generative(_no_clauseelement_condition)
  929. def select_from(self, from_obj):
  930. """Set the `from_obj` parameter of the query and return the newly
  931. resulting ``Query``. This replaces the table which this Query selects
  932. from with the given table.
  933. `from_obj` is a single table or selectable.
  934. """
  935. if isinstance(from_obj, (tuple, list)):
  936. # from_obj is actually a list again as of 0.5.3. so this restriction here
  937. # is somewhat artificial, but is still in place since select_from() implies aliasing all further
  938. # criterion against what's placed here, and its less complex to only
  939. # keep track of a single aliased FROM element being selected against. This could in theory be opened
  940. # up again to more complexity.
  941. util.warn_deprecated("select_from() now accepts a single Selectable as its argument, which replaces any existing FROM criterion.")
  942. from_obj = from_obj[-1]
  943. if not isinstance(from_obj, expression.FromClause):
  944. raise sa_exc.ArgumentError("select_from() accepts FromClause objects only.")
  945. self._set_select_from(from_obj)
  946. def __getitem__(self, item):
  947. if isinstance(item, slice):
  948. start, stop, step = util.decode_slice(item)
  949. if isinstance(stop, int) and isinstance(start, int) and stop - start <= 0:
  950. return []
  951. # perhaps we should execute a count() here so that we
  952. # can still use LIMIT/OFFSET ?
  953. elif (isinstance(start, int) and start < 0) \
  954. or (isinstance(stop, int) and stop < 0):
  955. return list(self)[item]
  956. res = self.slice(start, stop)
  957. if step is not None:
  958. return list(res)[None:None:item.step]
  959. else:
  960. return list(res)
  961. else:
  962. return list(self[item:item+1])[0]
  963. @_generative(_no_statement_condition)
  964. def slice(self, start, stop):
  965. """apply LIMIT/OFFSET to the ``Query`` based on a range and return the newly resulting ``Query``."""
  966. if start is not None and stop is not None:
  967. self._offset = (self._offset or 0) + start
  968. self._limit = stop - start
  969. elif start is None and stop is not None:
  970. self._limit = stop
  971. elif start is not None and stop is None:
  972. self._offset = (self._offset or 0) + start
  973. @_generative(_no_statement_condition)
  974. def limit(self, limit):
  975. """Apply a ``LIMIT`` to the query and return the newly resulting
  976. ``Query``.
  977. """
  978. self._limit = limit
  979. @_generative(_no_statement_condition)
  980. def offset(self, offset):
  981. """Apply an ``OFFSET`` to the query and return the newly resulting
  982. ``Query``.
  983. """
  984. self._offset = offset
  985. @_generative(_no_statement_condition)
  986. def distinct(self):
  987. """Apply a ``DISTINCT`` to the query and return the newly resulting
  988. ``Query``.
  989. """
  990. self._distinct = True
  991. def all(self):
  992. """Return the results represented by this ``Query`` as a list.
  993. This results in an execution of the underlying query.
  994. """
  995. return list(self)
  996. @_generative(_no_clauseelement_condition)
  997. def from_statement(self, statement):
  998. """Execute the given SELECT statement and return results.
  999. This method bypasses all internal statement compilation, and the
  1000. statement is executed without modification.
  1001. The statement argument is either a string, a ``select()`` construct,
  1002. or a ``text()`` construct, and should return the set of columns
  1003. appropriate to the entity class represented by this ``Query``.
  1004. Also see the ``instances()`` method.
  1005. """
  1006. if isinstance(statement, basestring):
  1007. statement = sql.text(statement)
  1008. if not isinstance(statement, (expression._TextClause, expression._SelectBaseMixin)):
  1009. raise sa_exc.ArgumentError("from_statement accepts text(), select(), and union() objects only.")
  1010. self._statement = statement
  1011. def first(self):
  1012. """Return the first result of this ``Query`` or None if the result doesn't contain any row.
  1013. This results in an execution of the underlying query.
  1014. """
  1015. if self._statement:
  1016. ret = list(self)[0:1]
  1017. else:
  1018. ret = list(self[0:1])
  1019. if len(ret) > 0:
  1020. return ret[0]
  1021. else:
  1022. return None
  1023. def one(self):
  1024. """Return exactly one result or raise an exception.
  1025. Raises ``sqlalchemy.orm.exc.NoResultFound`` if the query selects no rows.
  1026. Raises ``sqlalchemy.orm.exc.MultipleResultsFound`` if multiple rows are
  1027. selected.
  1028. This results in an execution of the underlying query.
  1029. """
  1030. if self._statement:
  1031. raise sa_exc.InvalidRequestError(
  1032. "one() not available when from_statement() is used; "
  1033. "use `first()` instead.")
  1034. ret = list(self[0:2])
  1035. if len(ret) == 1:
  1036. return ret[0]
  1037. elif len(ret) == 0:
  1038. raise orm_exc.NoResultFound("No row was found for one()")
  1039. else:
  1040. raise orm_exc.MultipleResultsFound(
  1041. "Multiple rows were found for one()")
  1042. def scalar(self):
  1043. """Return the first element of the first result or None.
  1044. >>> session.query(Item).scalar()
  1045. <Item>
  1046. >>> session.query(Item.id).scalar()
  1047. 1
  1048. >>> session.query(Item.id).filter(Item.id < 0).scalar()
  1049. None
  1050. >>> session.query(Item.id, Item.name).scalar()
  1051. 1
  1052. >>> session.query(func.count(Parent.id)).scalar()
  1053. 20
  1054. This results in an execution of the underlying query.
  1055. """
  1056. try:
  1057. ret = list(self)[0]
  1058. if not isinstance(ret, tuple):
  1059. return ret
  1060. return ret[0]
  1061. except IndexError:
  1062. return None
  1063. def __iter__(self):
  1064. context = self._compile_context()
  1065. context.statement.use_labels = True
  1066. if self._autoflush and not self._populate_existing:
  1067. self.session._autoflush()
  1068. return self._execute_and_instances(context)
  1069. def _execute_and_instances(self, querycontext):
  1070. result = self.session.execute(querycontext.statement, params=self._params, mapper=self._mapper_zero_or_none())
  1071. return self.instances(result, querycontext)
  1072. def instances(self, cursor, __context=None):
  1073. """Given a ResultProxy cursor as returned by connection.execute(), return an ORM result as an iterator.
  1074. e.g.::
  1075. result = engine.execute("select * from users")
  1076. for u in session.query(User).instances(result):
  1077. print u
  1078. """
  1079. session = self.session
  1080. context = __context
  1081. if context is None:
  1082. context = QueryContext(self)
  1083. context.runid = _new_runid()
  1084. filtered = bool(list(self._mapper_entities))
  1085. single_entity = filtered and len(self._entities) == 1
  1086. if filtered:
  1087. if single_entity:
  1088. filter = lambda x: util.unique_list(x, util.IdentitySet)
  1089. else:
  1090. filter = util.unique_list
  1091. else:
  1092. filter = None
  1093. custom_rows = single_entity and 'append_result' in self._entities[0].extension
  1094. (process, labels) = zip(*[query_entity.row_processor(self, context, custom_rows) for query_entity in self._entities])
  1095. if not single_entity:
  1096. labels = dict((label, property(itemgetter(i)))
  1097. for i, label in enumerate(labels)
  1098. if label)
  1099. rowtuple = type.__new__(type, "RowTuple", (tuple,), labels)
  1100. rowtuple.keys = labels.keys
  1101. while True:
  1102. context.progress = {}
  1103. context.partials = {}
  1104. if self._yield_per:
  1105. fetch = cursor.fetchmany(self._yield_per)
  1106. if not fetch:
  1107. break
  1108. else:
  1109. fetch = cursor.fetchall()
  1110. if custom_rows:
  1111. rows = []
  1112. for row in fetch:
  1113. process[0](context, row, rows)
  1114. elif single_entity:
  1115. rows = [process[0](context, row) for row in fetch]
  1116. else:
  1117. rows = [rowtuple(proc(context, row) for proc in process)
  1118. for row in fetch]
  1119. if filter:
  1120. rows = filter(rows)
  1121. if context.refresh_state and self._only_load_props and context.refresh_state in context.progress:
  1122. context.refresh_state.commit(context.refresh_state.dict, self._only_load_props)
  1123. context.progress.pop(context.refresh_state)
  1124. session._finalize_loaded(context.progress)
  1125. for ii, (dict_, attrs) in context.partials.items():
  1126. ii.commit(dict_, attrs)
  1127. for row in rows:
  1128. yield row
  1129. if not self._yield_per:
  1130. break
  1131. iterate_instances = util.deprecated()(instances)
  1132. def _get(self, key=None, ident=None, refresh_state=None, lockmode=None, only_load_props=None):
  1133. lockmode = lockmode or self._lockmode
  1134. if not self._populate_existing and not refresh_state and not self._mapper_zero().always_refresh and lockmode is None:
  1135. try:
  1136. instance = self.session.identity_map[key]
  1137. state = attributes.instance_state(instance)
  1138. if state.expired:
  1139. try:
  1140. state()
  1141. except orm_exc.ObjectDeletedError:
  1142. self.session._remove_newly_deleted(state)
  1143. return None
  1144. return instance
  1145. except KeyError:
  1146. pass
  1147. if ident is None:
  1148. if key is not None:
  1149. ident = key[1]
  1150. else:
  1151. ident = util.to_list(ident)
  1152. if refresh_state is None:
  1153. q = self._clone()
  1154. q._no_criterion_condition("get")
  1155. else:
  1156. q = self._clone()
  1157. if ident is not None:
  1158. mapper = q._mapper_zero()
  1159. params = {}
  1160. (_get_clause, _get_params) = mapper._get_clause
  1161. _get_clause = q._adapt_clause(_get_clause, True, False)
  1162. q._criterion = _get_clause
  1163. for i, primary_key in enumerate(mapper.primary_key):
  1164. try:
  1165. params[_get_params[primary_key].key] = ident[i]
  1166. except IndexError:
  1167. raise sa_exc.InvalidRequestError("Could not find enough values to formulate primary key for "
  1168. "query.get(); primary key columns are %s" % ', '.join("'%s'" % c for c in mapper.primary_key))
  1169. q._params = params
  1170. if lockmode is not None:
  1171. q._lockmode = lockmode
  1172. q._get_options(
  1173. populate_existing=bool(refresh_state),
  1174. version_check=(lockmode is not None),
  1175. only_load_props=only_load_props,
  1176. refresh_state=refresh_state)
  1177. q._order_by = None
  1178. try:
  1179. # call using all() to avoid LIMIT compilation complexity
  1180. return q.all()[0]
  1181. except IndexError:
  1182. return None
  1183. @property
  1184. def _select_args(self):
  1185. return {
  1186. 'limit':self._limit,
  1187. 'offset':self._offset,
  1188. 'distinct':self._distinct,
  1189. 'group_by':self._group_by or None,
  1190. 'having':self._having or None
  1191. }
  1192. @property
  1193. def _should_nest_selectable(self):
  1194. kwargs = self._select_args
  1195. return (kwargs.get('limit') is not None or
  1196. kwargs.get('offset') is not None or
  1197. kwargs.get('distinct', False))
  1198. def count(self):
  1199. """Return a count of rows this Query would return.
  1200. For simple entity queries, count() issues
  1201. a SELECT COUNT, and will specifically count the primary
  1202. key column of the first entity only. If the query uses
  1203. LIMIT, OFFSET, or DISTINCT, count() will wrap the statement
  1204. generated by this Query in a subquery, from which a SELECT COUNT
  1205. is issued, so that the contract of "how many rows
  1206. would be returned?" is honored.
  1207. For queries that request specific columns or expressions,
  1208. count() again makes no assumptions about those expressions
  1209. and will wrap everything in a subquery. Therefore,
  1210. ``Query.count()`` is usually not what you want in this case.
  1211. To count specific columns, often in conjunction with
  1212. GROUP BY, use ``func.count()`` as an individual column expression
  1213. instead of ``Query.count()``. See the ORM tutorial
  1214. for an example.
  1215. """
  1216. should_nest = [self._should_nest_selectable]
  1217. def ent_cols(ent):
  1218. if isinstance(ent, _MapperEntity):
  1219. return ent.mapper.primary_key
  1220. else:
  1221. should_nest[0] = True
  1222. return [ent.column]
  1223. return self._col_aggregate(sql.literal_column('1'), sql.func.count,
  1224. nested_cols=chain(*[ent_cols(ent) for ent in self._entities]),
  1225. should_nest = should_nest[0]
  1226. )
  1227. def _col_aggregate(self, col, func, nested_cols=None, should_nest=False):
  1228. context = QueryContext(self)
  1229. for entity in self._entities:
  1230. entity.setup_context(self, context)
  1231. if context.from_clause:
  1232. from_obj = list(context.from_clause)
  1233. else:
  1234. from_obj = context.froms
  1235. self._adjust_for_single_inheritance(context)
  1236. whereclause = context.whereclause
  1237. if should_nest:
  1238. if not nested_cols:
  1239. nested_cols = [col]
  1240. else:
  1241. nested_cols = list(nested_cols)
  1242. s = sql.select(nested_cols, whereclause, from_obj=from_obj, use_labels=True, **self._select_args)
  1243. s = s.alias()
  1244. s = sql.select([func(s.corresponding_column(col) or col)]).select_from(s)
  1245. else:
  1246. s = sql.select([func(col)], whereclause, from_obj=from_obj, **self._select_args)
  1247. if self._autoflush and not self._populate_existing:
  1248. self.session._autoflush()
  1249. return self.session.scalar(s, params=self._params, mapper=self._mapper_zero())
  1250. def delete(self, synchronize_session='fetch'):
  1251. """Perform a bulk delete query.
  1252. Deletes rows matched by this query from the database.
  1253. :param synchronize_session: chooses the strategy for the removal of matched
  1254. objects from the session. Valid values are:
  1255. False
  1256. don't synchronize the session. This option is the most efficient and is reliable
  1257. once the session is expired, which typically occurs after a commit(). Before
  1258. the expiration, objects may still remain in the session which were in fact deleted
  1259. which can lead to confusing results if they are accessed via get() or already
  1260. loaded collections.
  1261. 'fetch'
  1262. performs a select query before the delete to find objects that are matched
  1263. by the delete query and need to be removed from the session. Matched objects
  1264. are removed from the session. 'fetch' is the default strategy.
  1265. 'evaluate'
  1266. experimental feature. Tries to evaluate the querys criteria in Python
  1267. straight on the objects in the session. If evaluation of the criteria isn't
  1268. implemented, the 'fetch' strategy will be used as a fallback.
  1269. The expression evaluator currently doesn't account for differing string
  1270. collations between the database and Python.
  1271. Returns the number of rows deleted, excluding any cascades.
  1272. The method does *not* offer in-Python cascading of relations - it is assumed that
  1273. ON DELETE CASCADE is configured for any foreign key references which require it.
  1274. The Session needs to be expired (occurs automatically after commit(), or call expire_all())
  1275. in order for the state of dependent objects subject to delete or delete-orphan cascade to be
  1276. correctly represented.
  1277. Also, the ``before_delete()`` and ``after_delete()`` :class:`~sqlalchemy.orm.interfaces.MapperExtension`
  1278. methods are not called from this method. For a delete hook here, use the
  1279. ``after_bulk_delete()`` :class:`~sqlalchemy.orm.interfaces.MapperExtension` method.
  1280. """
  1281. #TODO: lots of duplication and ifs - probably needs to be refactored to strategies
  1282. #TODO: cascades need handling.
  1283. if synchronize_session not in [False, 'evaluate', 'fetch']:
  1284. raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'fetch'")
  1285. self._no_select_modifiers("delete")
  1286. self = self.enable_eagerloads(False)
  1287. context = self._compile_context()
  1288. if len(context.statement.froms) != 1 or not isinstance(context.statement.froms[0], schema.Table):
  1289. raise sa_exc.ArgumentError("Only deletion via a single table query is currently supported")
  1290. primary_table = context.statement.froms[0]
  1291. session = self.session
  1292. if synchronize_session == 'evaluate':
  1293. try:
  1294. evaluator_compiler = evaluator.EvaluatorCompiler()
  1295. eval_condition = evaluator_compiler.process(self.whereclause)
  1296. except evaluator.UnevaluatableError:
  1297. synchronize_session = 'fetch'
  1298. delete_stmt = sql.delete(primary_table, context.whereclause)
  1299. if synchronize_session == 'fetch':
  1300. #TODO: use RETURNING when available
  1301. select_stmt = context.statement.with_only_columns(primary_table.primary_key)
  1302. matched_rows = session.execute(select_stmt, params=self._params).fetchall()
  1303. if self._autoflush:
  1304. session._autoflush()
  1305. result = session.execute(delete_stmt, params=self._params)
  1306. if synchronize_session == 'evaluate':
  1307. target_cls = self._mapper_zero().class_
  1308. #TODO: detect when the where clause is a trivial primary key match
  1309. objs_to_expunge = [obj for (cls, pk),obj in session.identity_map.iteritems()
  1310. if issubclass(cls, target_cls) and eval_condition(obj)]
  1311. for obj in objs_to_expunge:
  1312. session._remove_newly_deleted(attributes.instance_state(obj))
  1313. elif synchronize_session == 'fetch':
  1314. target_mapper = self._mapper_zero()
  1315. for primary_key in matched_rows:
  1316. identity_key = target_mapper.identity_key_from_primary_key(list(primary_key))
  1317. if identity_key in session.identity_map:
  1318. session._remove_newly_deleted(attributes.instance_state(session.identity_map[identity_key]))
  1319. for ext in session.extensions:
  1320. ext.after_bulk_delete(session, self, context, result)
  1321. return result.rowcount
  1322. def update(self, values, synchronize_session='expire'):
  1323. """Perform a bulk update query.
  1324. Updates rows matched by this query in the database.
  1325. :param values: a dictionary with attributes names as keys and literal values or sql expressions
  1326. as values.
  1327. :param synchronize_session: chooses the strategy to update the
  1328. attributes on objects in the session. Valid values are:
  1329. False
  1330. don't synchronize the session. Use this when you don't need to use the
  1331. session after the update or you can be sure that none of the matched objects
  1332. are in the session.
  1333. 'expire'
  1334. performs a select query before the update to find objects that are matched
  1335. by the update query. The updated attributes are expired on matched objects.
  1336. 'evaluate'
  1337. experimental feature. Tries to evaluate the querys criteria in Python
  1338. straight on the objects in the session. If evaluation of the criteria isn't
  1339. implemented, the 'expire' strategy will be used as a fallback.
  1340. The expression evaluator currently doesn't account for differing string
  1341. collations between the database and Python.
  1342. Returns the number of rows matched by the update.
  1343. The method does *not* offer in-Python cascading of relations - it is assumed that
  1344. ON UPDATE CASCADE is configured for any foreign key references which require it.
  1345. The Session needs to be expired (occurs automatically after commit(), or call expire_all())
  1346. in order for the state of dependent objects subject foreign key cascade to be
  1347. correctly represented.
  1348. Also, the ``before_update()`` and ``after_update()`` :class:`~sqlalchemy.orm.interfaces.MapperExtension`
  1349. methods are not called from this method. For an update hook here, use the
  1350. ``after_bulk_update()`` :class:`~sqlalchemy.orm.interfaces.SessionExtension` method.
  1351. """
  1352. #TODO: value keys need to be mapped to corresponding sql cols and instr.attr.s to string keys
  1353. #TODO: updates of manytoone relations need to be converted to fk assignments
  1354. #TODO: cascades need handling.
  1355. self._no_select_modifiers("update")
  1356. if synchronize_session not in [False, 'evaluate', 'expire']:
  1357. raise sa_exc.ArgumentError("Valid strategies for session synchronization are False, 'evaluate' and 'expire'")
  1358. self = self.enable_eagerloads(False)
  1359. context = self._compile_context()
  1360. if len(context.statement.froms) != 1 or not isinstance(context.statement.froms[0], schema.Table):
  1361. raise sa_exc.ArgumentError("Only update via a single table query is currently supported")
  1362. primary_table = context.statement.froms[0]
  1363. session = self.session
  1364. if synchronize_session == 'evaluate':
  1365. try:
  1366. evaluator_compiler = evaluator.EvaluatorCompiler()
  1367. eval_condition = evaluator_compiler.process(self.whereclause)
  1368. value_evaluators = {}
  1369. for key,value in values.items():
  1370. key = expression._column_as_key(key)
  1371. value_evaluators[key] = evaluator_compiler.process(expression._literal_as_binds(value))
  1372. except evaluator.UnevaluatableError:
  1373. synchronize_session = 'expire'
  1374. update_stmt = sql.update(primary_table, context.whereclause, values)
  1375. if synchronize_session == 'expire':
  1376. select_stmt = context.statement.with_only_columns(primary_table.primary_key)
  1377. matched_rows = session.execute(select_stmt, params=self._params).fetchall()
  1378. if self._autoflush:
  1379. session._autoflush()
  1380. result = session.execute(update_stmt, params=self._params)
  1381. if synchronize_session == 'evaluate':
  1382. target_cls = self._mapper_zero().class_
  1383. for (cls, pk),obj in session.identity_map.iteritems():
  1384. evaluated_keys = value_evaluators.keys()
  1385. if issubclass(cls, target_cls) and eval_condition(obj):
  1386. state, dict_ = attributes.instance_state(obj), attributes.instance_dict(obj)
  1387. # only evaluate unmodified attributes
  1388. to_evaluate = state.unmodified.intersection(evaluated_keys)
  1389. for key in to_evaluate:
  1390. dict_[key] = value_evaluators[key](obj)
  1391. state.commit(dict_, list(to_evaluate))
  1392. # expire attributes with pending changes (there was no autoflush, so they are overwritten)
  1393. state.expire_attributes(set(evaluated_keys).difference(to_evaluate))
  1394. elif synchronize_session == 'expire':
  1395. target_mapper = self._mapper_zero()
  1396. for primary_key in matched_rows:
  1397. identity_key = target_mapper.identity_key_from_primary_key(list(primary_key))
  1398. if identity_key in session.identity_map:
  1399. session.expire(session.identity_map[identity_key], [expression._column_as_key(k) for k in values])
  1400. for ext in session.extensions:
  1401. ext.after_bulk_update(session, self, context, result)
  1402. return result.rowcount
  1403. def _compile_context(self, labels=True):
  1404. context = QueryContext(self)
  1405. if context.statement:
  1406. return context
  1407. if self._lockmode:
  1408. try:
  1409. for_update = {'read': 'read',
  1410. 'update': True,
  1411. 'update_nowait': 'nowait',
  1412. None: False}[self._lockmode]
  1413. except KeyError:
  1414. raise sa_exc.ArgumentError("Unknown lockmode '%s'" % self._lockmode)
  1415. else:
  1416. for_update = False
  1417. for entity in self._entities:
  1418. entity.setup_context(self, context)
  1419. eager_joins = context.eager_joins.values()
  1420. if context.from_clause:
  1421. froms = list(context.from_clause) # "load from explicit FROMs" mode, i.e. when select_from() or join() is used
  1422. else:
  1423. froms = context.froms # "load from discrete FROMs" mode, i.e. when each _MappedEntity has its own FROM
  1424. self._adjust_for_single_inheritance(context)
  1425. if not context.primary_columns:
  1426. if self._only_load_props:
  1427. raise sa_exc.InvalidRequestError("No column-based properties specified for refresh operation."
  1428. " Use session.expire() to reload collections and related items.")
  1429. else:
  1430. raise sa_exc.InvalidRequestError("Query contains no columns with which to SELECT from.")
  1431. if eager_joins and self._should_nest_selectable:
  1432. # for eager joins present and LIMIT/OFFSET/DISTINCT, wrap the query inside a select,
  1433. # then append eager joins onto that
  1434. if context.order_by:
  1435. order_by_col_expr = list(chain(*[sql_util.find_columns(o) for o in context.order_by]))
  1436. else:
  1437. context.order_by = None
  1438. order_by_col_expr = []
  1439. inner = sql.select(
  1440. context.primary_columns + order_by_col_expr,
  1441. context.whereclause,
  1442. from_obj=froms,
  1443. use_labels=labels,
  1444. correlate=False,
  1445. order_by=context.order_by,
  1446. **self._select_args
  1447. )
  1448. if self._correlate:
  1449. inner = inner.correlate(*self._correlate)
  1450. inner = inner.alias()
  1451. equivs = self.__all_equivs()
  1452. context.adapter = sql_util.ColumnAdapter(inner, equivs)
  1453. statement = sql.select([inner] + context.secondary_columns, for_update=for_update, use_labels=labels)
  1454. from_clause = inner
  1455. for eager_join in eager_joins:
  1456. # EagerLoader places a 'stop_on' attribute on the join,
  1457. # giving us a marker as to where the "splice point" of the join should be
  1458. from_clause = sql_util.splice_joins(from_clause, eager_join, eager_join.stop_on)
  1459. statement.append_from(from_clause)
  1460. if context.order_by:
  1461. statement.append_order_by(*context.adapter.copy_and_process(context.order_by))
  1462. statement.append_order_by(*context.eager_order_by)
  1463. else:
  1464. if not context.order_by:
  1465. context.order_by = None
  1466. if self._distinct and context.order_by:
  1467. order_by_col_expr = list(chain(*[sql_util.find_columns(o) for o in context.order_by]))
  1468. context.primary_columns += order_by_col_expr
  1469. froms += context.eager_joins.values()
  1470. statement = sql.select(
  1471. context.primary_columns + context.secondary_columns,
  1472. context.whereclause,
  1473. from_obj=froms,
  1474. use_labels=labels,
  1475. for_update=for_update,
  1476. correlate=False,
  1477. order_by=context.order_by,
  1478. **self._select_args
  1479. )
  1480. if self._correlate:
  1481. statement = statement.correlate(*self._correlate)
  1482. if context.eager_order_by:
  1483. statement.append_order_by(*context.eager_order_by)
  1484. context.statement = statement
  1485. return context
  1486. def _adjust_for_single_inheritance(self, context):
  1487. """Apply single-table-inheritance filtering.
  1488. For all distinct single-table-inheritance mappers represented in the
  1489. columns clause of this query, add criterion to the WHERE clause of the
  1490. given QueryContext such that only the appropriate subtypes are
  1491. selected from the total results.
  1492. """
  1493. for entity, (mapper, adapter, s, i, w) in self._mapper_adapter_map.iteritems():
  1494. single_crit = mapper._single_table_criterion
  1495. if single_crit:
  1496. if adapter:
  1497. single_crit = adapter.traverse(single_crit)
  1498. single_crit = self._adapt_clause(single_crit, False, False)
  1499. context.whereclause = sql.and_(context.whereclause, single_crit)
  1500. def __str__(self):
  1501. return str(self._compile_context().statement)
  1502. class _QueryEntity(object):
  1503. """represent an entity column returned within a Query result."""
  1504. def __new__(cls, *args, **kwargs):
  1505. if cls is _QueryEntity:
  1506. entity = args[1]
  1507. if not isinstance(entity, basestring) and _is_mapped_class(entity):
  1508. cls = _MapperEntity
  1509. else:
  1510. cls = _ColumnEntity
  1511. return object.__new__(cls)
  1512. def _clone(self):
  1513. q = self.__class__.__new__(self.__class__)
  1514. q.__dict__ = self.__dict__.copy()
  1515. return q
  1516. class _MapperEntity(_QueryEntity):
  1517. """mapper/class/AliasedClass entity"""
  1518. def __init__(self, query, entity):
  1519. self.primary_entity = not query._entities
  1520. query._entities.append(self)
  1521. self.entities = [entity]
  1522. self.entity_zero = entity
  1523. def setup_entity(self, entity, mapper, adapter, from_obj, is_aliased_class, with_polymorphic):
  1524. self.mapper = mapper
  1525. self.extension = self.mapper.extension
  1526. self.adapter = adapter
  1527. self.selectable = from_obj
  1528. self._with_polymorphic = with_polymorphic
  1529. self._polymorphic_discriminator = None
  1530. self.is_aliased_class = is_aliased_class
  1531. if is_aliased_class:
  1532. self.path_entity = self.entity = self.entity_zero = entity
  1533. else:
  1534. self.path_entity = mapper.base_mapper
  1535. self.entity = self.entity_zero = mapper
  1536. def set_with_polymorphic(self, query, cls_or_mappers, selectable, discriminator):
  1537. if cls_or_mappers is None:
  1538. query._reset_polymorphic_adapter(self.mapper)
  1539. return
  1540. mappers, from_obj = self.mapper._with_polymorphic_args(cls_or_mappers, selectable)
  1541. self._with_polymorphic = mappers
  1542. self._polymorphic_discriminator = discriminator
  1543. # TODO: do the wrapped thing here too so that with_polymorphic() can be
  1544. # applied to aliases
  1545. if not self.is_aliased_class:
  1546. self.selectable = from_obj
  1547. self.adapter = query._get_polymorphic_adapter(self, from_obj)
  1548. def corresponds_to(self, entity):
  1549. if _is_aliased_class(entity):
  1550. return entity is self.path_entity
  1551. else:
  1552. return entity.base_mapper is self.path_entity
  1553. def _get_entity_clauses(self, query, context):
  1554. adapter = None
  1555. if not self.is_aliased_class and query._polymorphic_adapters:
  1556. adapter = query._polymorphic_adapters.get(self.mapper, None)
  1557. if not adapter and self.adapter:
  1558. adapter = self.adapter
  1559. if adapter:
  1560. if query._from_obj_alias:
  1561. ret = adapter.wrap(query._from_obj_alias)
  1562. else:
  1563. ret = adapter
  1564. else:
  1565. ret = query._from_obj_alias
  1566. return ret
  1567. def row_processor(self, query, context, custom_rows):
  1568. adapter = self._get_entity_clauses(query, context)
  1569. if context.adapter and adapter:
  1570. adapter = adapter.wrap(context.adapter)
  1571. elif not adapter:
  1572. adapter = context.adapter
  1573. # polymorphic mappers which have concrete tables in their hierarchy usually
  1574. # require row aliasing unconditionally.
  1575. if not adapter and self.mapper._requires_row_aliasing:
  1576. adapter = sql_util.ColumnAdapter(self.selectable, self.mapper._equivalent_columns)
  1577. if self.primary_entity:
  1578. _instance = self.mapper._instance_processor(context, (self.path_entity,), adapter,
  1579. extension=self.extension, only_load_props=query._only_load_props, refresh_state=context.refresh_state,
  1580. polymorphic_discriminator=self._polymorphic_discriminator
  1581. )
  1582. else:
  1583. _instance = self.mapper._instance_processor(context, (self.path_entity,), adapter,
  1584. polymorphic_discriminator=self._polymorphic_discriminator)
  1585. if custom_rows:
  1586. def main(context, row, result):
  1587. _instance(row, result)
  1588. else:
  1589. def main(context, row):
  1590. return _instance(row, None)
  1591. if self.is_aliased_class:
  1592. entname = self.entity._sa_label_name
  1593. else:
  1594. entname = self.mapper.class_.__name__
  1595. return main, entname
  1596. def setup_context(self, query, context):
  1597. adapter = self._get_entity_clauses(query, context)
  1598. context.froms.append(self.selectable)
  1599. if context.order_by is False and self.mapper.order_by:
  1600. context.order_by = self.mapper.order_by
  1601. # apply adaptation to the mapper's order_by if needed.
  1602. if adapter:
  1603. context.order_by = adapter.adapt_list(util.to_list(context.order_by))
  1604. for value in self.mapper._iterate_polymorphic_properties(self._with_polymorphic):
  1605. if query._only_load_props and value.key not in query._only_load_props:
  1606. continue
  1607. value.setup(
  1608. context,
  1609. self,
  1610. (self.path_entity,),
  1611. adapter,
  1612. only_load_props=query._only_load_props,
  1613. column_collection=context.primary_columns
  1614. )
  1615. if self._polymorphic_discriminator:
  1616. if adapter:
  1617. pd = adapter.columns[self._polymorphic_discriminator]
  1618. else:
  1619. pd = self._polymorphic_discriminator
  1620. context.primary_columns.append(pd)
  1621. def __str__(self):
  1622. return str(self.mapper)
  1623. class _ColumnEntity(_QueryEntity):
  1624. """Column/expression based entity."""
  1625. def __init__(self, query, column):
  1626. if isinstance(column, basestring):
  1627. column = sql.literal_column(column)
  1628. self._result_label = column.name
  1629. elif isinstance(column, attributes.QueryableAttribute):
  1630. self._result_label = column.key
  1631. column = column.__clause_element__()
  1632. else:
  1633. self._result_label = getattr(column, 'key', None)
  1634. if not isinstance(column, expression.ColumnElement) and hasattr(column, '_select_iterable'):
  1635. for c in column._select_iterable:
  1636. if c is column:
  1637. break
  1638. _ColumnEntity(query, c)
  1639. if c is not column:
  1640. return
  1641. if not isinstance(column, sql.ColumnElement):
  1642. raise sa_exc.InvalidRequestError(
  1643. "SQL expression, column, or mapped entity expected - got '%r'" % column
  1644. )
  1645. # if the Column is unnamed, give it a
  1646. # label() so that mutable column expressions
  1647. # can be located in the result even
  1648. # if the expression's identity has been changed
  1649. # due to adaption
  1650. if not column._label:
  1651. column = column.label(None)
  1652. query._entities.append(self)
  1653. self.column = column
  1654. self.froms = set()
  1655. # look for ORM entities represented within the
  1656. # given expression. Try to count only entities
  1657. # for columns whos FROM object is in the actual list
  1658. # of FROMs for the overall expression - this helps
  1659. # subqueries which were built from ORM constructs from
  1660. # leaking out their entities into the main select construct
  1661. actual_froms = set(column._from_objects)
  1662. self.entities = util.OrderedSet(
  1663. elem._annotations['parententity']
  1664. for elem in visitors.iterate(column, {})
  1665. if 'parententity' in elem._annotations
  1666. and actual_froms.intersection(elem._from_objects)
  1667. )
  1668. if self.entities:
  1669. self.entity_zero = list(self.entities)[0]
  1670. else:
  1671. self.entity_zero = None
  1672. def setup_entity(self, entity, mapper, adapter, from_obj, is_aliased_class, with_polymorphic):
  1673. self.selectable = from_obj
  1674. self.froms.add(from_obj)
  1675. def corresponds_to(self, entity):
  1676. if self.entity_zero is None:
  1677. return False
  1678. elif _is_aliased_class(entity):
  1679. return entity is self.entity_zero
  1680. else:
  1681. return not _is_aliased_class(self.entity_zero) and \
  1682. entity.base_mapper.common_parent(self.entity_zero)
  1683. def _resolve_expr_against_query_aliases(self, query, expr, context):
  1684. return query._adapt_clause(expr, False, True)
  1685. def row_processor(self, query, context, custom_rows):
  1686. column = self._resolve_expr_against_query_aliases(query, self.column, context)
  1687. if context.adapter:
  1688. column = context.adapter.columns[column]
  1689. def proc(context, row):
  1690. return row[column]
  1691. return (proc, self._result_label)
  1692. def setup_context(self, query, context):
  1693. column = self._resolve_expr_against_query_aliases(query, self.column, context)
  1694. context.froms += list(self.froms)
  1695. context.primary_columns.append(column)
  1696. def __str__(self):
  1697. return str(self.column)
  1698. log.class_logger(Query)
  1699. class QueryContext(object):
  1700. def __init__(self, query):
  1701. if query._statement:
  1702. if isinstance(query._statement, expression._SelectBaseMixin) and not query._statement.use_labels:
  1703. self.statement = query._statement.apply_labels()
  1704. else:
  1705. self.statement = query._statement
  1706. else:
  1707. self.statement = None
  1708. self.from_clause = query._from_obj
  1709. self.whereclause = query._criterion
  1710. self.order_by = query._order_by
  1711. if self.order_by:
  1712. self.order_by = [expression._literal_as_text(o) for o in util.to_list(self.order_by)]
  1713. self.query = query
  1714. self.session = query.session
  1715. self.populate_existing = query._populate_existing
  1716. self.version_check = query._version_check
  1717. self.refresh_state = query._refresh_state
  1718. self.primary_columns = []
  1719. self.secondary_columns = []
  1720. self.eager_order_by = []
  1721. self.enable_eagerloads = query._enable_eagerloads
  1722. self.eager_joins = {}
  1723. self.froms = []
  1724. self.adapter = None
  1725. self.options = set(query._with_options)
  1726. self.propagate_options = self.options.difference(o for o in self.options if not o.propagate_to_loaders)
  1727. self.attributes = query._attributes.copy()
  1728. class AliasOption(interfaces.MapperOption):
  1729. def __init__(self, alias):
  1730. self.alias = alias
  1731. def process_query(self, query):
  1732. if isinstance(self.alias, basestring):
  1733. alias = query._mapper_zero().mapped_table.alias(self.alias)
  1734. else:
  1735. alias = self.alias
  1736. query._from_obj_alias = sql_util.ColumnAdapter(alias)
  1737. _runid = 1L
  1738. _id_lock = util.threading.Lock()
  1739. def _new_runid():
  1740. global _runid
  1741. _id_lock.acquire()
  1742. try:
  1743. _runid += 1
  1744. return _runid
  1745. finally:
  1746. _id_lock.release()