PageRenderTime 86ms CodeModel.GetById 30ms 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

Large files files are truncated, but you can click here to view the full 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._s

Large files files are truncated, but you can click here to view the full file