diff options
Diffstat (limited to 'lib/sqlalchemy/orm/query.py')
| -rw-r--r-- | lib/sqlalchemy/orm/query.py | 58 |
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 |
