summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/query.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm/query.py')
-rw-r--r--lib/sqlalchemy/orm/query.py58
1 files changed, 39 insertions, 19 deletions
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index a91947ac3..5afd3e1b6 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -8,15 +8,16 @@ import session as sessionlib
from sqlalchemy import sql, util, exceptions, sql_util
import mapper
-
+from interfaces import OperationContext
class Query(object):
"""encapsulates the object-fetching operations provided by Mappers."""
- def __init__(self, class_or_mapper, session=None, entity_name=None, lockmode=None, **kwargs):
+ def __init__(self, class_or_mapper, session=None, entity_name=None, lockmode=None, with_options=None, **kwargs):
if isinstance(class_or_mapper, type):
self.mapper = mapper.class_mapper(class_or_mapper, entity_name=entity_name)
else:
self.mapper = class_or_mapper.compile()
+ self.with_options = with_options or []
self.mapper = self.mapper.get_select_mapper().compile()
self.always_refresh = kwargs.pop('always_refresh', self.mapper.always_refresh)
self.order_by = kwargs.pop('order_by', self.mapper.order_by)
@@ -245,7 +246,7 @@ class Query(object):
def options(self, *args, **kwargs):
"""returns a new Query object using the given MapperOptions."""
- return self.mapper.options(*args, **kwargs).using(session=self._session)
+ return Query(self.mapper, self._session, with_options=args)
def with_lockmode(self, mode):
"""return a new Query object with the specified locking mode."""
@@ -268,7 +269,7 @@ class Query(object):
def instances(self, clauseelement, params=None, *args, **kwargs):
result = self.session.execute(self.mapper, clauseelement, params=params)
try:
- return self.mapper.instances(result, self.session, **kwargs)
+ return self.mapper.instances(result, self.session, with_options=self.with_options, **kwargs)
finally:
result.close()
@@ -307,23 +308,29 @@ class Query(object):
params = {}
return self.instances(statement, params=params, **kwargs)
- def _should_nest(self, **kwargs):
+ def _should_nest(self, querycontext):
"""return True if the given statement options indicate that we should "nest" the
generated query as a subquery inside of a larger eager-loading query. this is used
with keywords like distinct, limit and offset and the mapper defines eager loads."""
return (
self.mapper.has_eager()
- and self._nestable(**kwargs)
+ and self._nestable(**querycontext.select_args())
)
def _nestable(self, **kwargs):
"""return true if the given statement options imply it should be nested."""
- return (kwargs.has_key('limit') or kwargs.has_key('offset') or kwargs.get('distinct', False))
+ return (kwargs.get('limit') is not None or kwargs.get('offset') is not None or kwargs.get('distinct', False))
def compile(self, whereclause = None, **kwargs):
- order_by = kwargs.pop('order_by', False)
- from_obj = kwargs.pop('from_obj', [])
- lockmode = kwargs.pop('lockmode', self.lockmode)
+ context = kwargs.pop('query_context', None)
+ if context is None:
+ context = QueryContext(self, kwargs)
+ order_by = context.order_by
+ from_obj = context.from_obj
+ lockmode = context.lockmode
+ distinct = context.distinct
+ limit = context.limit
+ offset = context.offset
if order_by is False:
order_by = self.order_by
if order_by is False:
@@ -345,7 +352,7 @@ class Query(object):
if self.table not in alltables:
from_obj.append(self.table)
- if self._should_nest(**kwargs):
+ if self._should_nest(context):
# if theres an order by, add those columns to the column list
# of the "rowcount" query we're going to make
if order_by:
@@ -355,17 +362,14 @@ class Query(object):
else:
cf = []
- s2 = sql.select(self.table.primary_key + list(cf), whereclause, use_labels=True, from_obj=from_obj, **kwargs)
-# raise "ok first thing", str(s2)
- if not kwargs.get('distinct', False) and order_by:
+ s2 = sql.select(self.table.primary_key + list(cf), whereclause, use_labels=True, from_obj=from_obj, **context.select_args())
+ if not distinct and order_by:
s2.order_by(*util.to_list(order_by))
s3 = s2.alias('tbl_row_count')
crit = []
for i in range(0, len(self.table.primary_key)):
crit.append(s3.primary_key[i] == self.table.primary_key[i])
statement = sql.select([], sql.and_(*crit), from_obj=[self.table], use_labels=True, for_update=for_update)
- # raise "OK statement", str(statement)
-
# now for the order by, convert the columns to their corresponding columns
# in the "rowcount" query, and tack that new order by onto the "rowcount" query
if order_by:
@@ -377,7 +381,7 @@ class Query(object):
[o.accept_visitor(aliasizer) for o in order_by]
statement.order_by(*util.to_list(order_by))
else:
- statement = sql.select([], whereclause, from_obj=from_obj, use_labels=True, for_update=for_update, **kwargs)
+ statement = sql.select([], whereclause, from_obj=from_obj, use_labels=True, for_update=for_update, **context.select_args())
if order_by:
statement.order_by(*util.to_list(order_by))
# for a DISTINCT query, you need the columns explicitly specified in order
@@ -385,11 +389,27 @@ class Query(object):
# TODO: this should be done at the SQL level not the mapper level
if kwargs.get('distinct', False) and order_by:
[statement.append_column(c) for c in util.to_list(order_by)]
- # plugin point
+ context.statement = statement
# give all the attached properties a chance to modify the query
for value in self.mapper.props.values():
- value.setup(statement, **kwargs)
+ value.setup(context)
return statement
+class QueryContext(OperationContext):
+ """created within the Query.compile() method to store and share
+ state among all the Mappers and MapperProperty objects used in a query construction."""
+ def __init__(self, query, kwargs):
+ self.query = query
+ self.order_by = kwargs.pop('order_by', False)
+ self.from_obj = kwargs.pop('from_obj', [])
+ self.lockmode = kwargs.pop('lockmode', query.lockmode)
+ self.distinct = kwargs.pop('distinct', False)
+ self.limit = kwargs.pop('limit', None)
+ self.offset = kwargs.pop('offset', None)
+ self.statement = None
+ super(QueryContext, self).__init__(query.mapper, query.with_options, **kwargs)
+ def select_args(self):
+ return {'limit':self.limit, 'offset':self.offset, 'distinct':self.distinct}
+ \ No newline at end of file