diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-22 20:15:50 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-22 20:15:50 -0400 |
commit | 0197a70343a7d6f4eb387bbd461e0624e2dc436b (patch) | |
tree | 1da79ee20f5910f40bf4490f2e5b84a1d25e2908 /lib/sqlalchemy/orm/interfaces.py | |
parent | efcc9d782274ee1d5eb08855d50aaf627d76a073 (diff) | |
download | sqlalchemy-0197a70343a7d6f4eb387bbd461e0624e2dc436b.tar.gz |
- Fixed bug which affected all eagerload() and similar options
such that "remote" eager loads, i.e. eagerloads off of a lazy
load such as query(A).options(eagerload(A.b, B.c))
wouldn't eagerload anything, but using eagerload("b.c") would
work fine.
- subquery eagerloading very close
Diffstat (limited to 'lib/sqlalchemy/orm/interfaces.py')
-rw-r--r-- | lib/sqlalchemy/orm/interfaces.py | 150 |
1 files changed, 80 insertions, 70 deletions
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index c773e74f6..255b6b6fe 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -757,14 +757,35 @@ class PropertyOption(MapperOption): self._process(query, False) def _process(self, query, raiseerr): - paths, mappers = self.__get_paths(query, raiseerr) + paths, mappers = self._get_paths(query, raiseerr) if paths: self.process_query_property(query, paths, mappers) def process_query_property(self, query, paths, mappers): pass - def __find_entity(self, query, mapper, raiseerr): + def __getstate__(self): + d = self.__dict__.copy() + d['key'] = ret = [] + for token in util.to_list(self.key): + if isinstance(token, PropComparator): + ret.append((token.mapper.class_, token.key)) + else: + ret.append(token) + return d + + def __setstate__(self, state): + ret = [] + for key in state['key']: + if isinstance(key, tuple): + cls, propkey = key + ret.append(getattr(cls, propkey)) + else: + ret.append(key) + state['key'] = tuple(ret) + self.__dict__ = state + + def _find_entity(self, query, mapper, raiseerr): from sqlalchemy.orm.util import _class_to_mapper, _is_aliased_class if _is_aliased_class(mapper): @@ -773,7 +794,7 @@ class PropertyOption(MapperOption): else: searchfor = _class_to_mapper(mapper) isa = True - + for ent in query._mapper_entities: if searchfor is ent.path_entity or ( isa and @@ -789,28 +810,7 @@ class PropertyOption(MapperOption): else: return None - def __getstate__(self): - d = self.__dict__.copy() - d['key'] = ret = [] - for token in util.to_list(self.key): - if isinstance(token, PropComparator): - ret.append((token.mapper.class_, token.key)) - else: - ret.append(token) - return d - - def __setstate__(self, state): - ret = [] - for key in state['key']: - if isinstance(key, tuple): - cls, propkey = key - ret.append(getattr(cls, propkey)) - else: - ret.append(key) - state['key'] = tuple(ret) - self.__dict__ = state - - def __get_paths(self, query, raiseerr): + def _get_paths(self, query, raiseerr): path = None entity = None l = [] @@ -820,61 +820,71 @@ class PropertyOption(MapperOption): # with an existing path current_path = list(query._current_path) - if self.mapper: - entity = self.__find_entity(query, self.mapper, raiseerr) - mapper = entity.mapper - path_element = entity.path_entity - + tokens = [] for key in util.to_list(self.key): if isinstance(key, basestring): - tokens = key.split('.') + tokens += key.split('.') else: - tokens = [key] - for token in tokens: - if isinstance(token, basestring): - if not entity: - entity = query._entity_zero() - path_element = entity.path_entity - mapper = entity.mapper - mappers.append(mapper) - prop = mapper.get_property(token, resolve_synonyms=True, raiseerr=raiseerr) - key = token - elif isinstance(token, PropComparator): - prop = token.property - if not entity: - entity = self.__find_entity(query, token.parententity, raiseerr) - if not entity: - return [], [] - path_element = entity.path_entity - mappers.append(prop.parent) - key = prop.key - else: - raise sa_exc.ArgumentError("mapper option expects string key " - "or list of attributes") - - if current_path and key == current_path[1]: - current_path = current_path[2:] - continue + tokens += [key] + + for token in tokens: + if isinstance(token, basestring): + if not entity: + if current_path: + if current_path[1] == token: + current_path = current_path[2:] + continue - if prop is None: - return [], [] - - path = build_path(path_element, prop.key, path) - l.append(path) - if getattr(token, '_of_type', None): - path_element = mapper = token._of_type - else: - path_element = mapper = getattr(prop, 'mapper', None) - - if path_element: - path_element = path_element + entity = query._entity_zero() + path_element = entity.path_entity + mapper = entity.mapper + mappers.append(mapper) + prop = mapper.get_property( + token, + resolve_synonyms=True, + raiseerr=raiseerr) + key = token + elif isinstance(token, PropComparator): + prop = token.property + if not entity: + if current_path: + if current_path[0:2] == [token.parententity, prop.key]: + current_path = current_path[2:] + continue + + entity = self._find_entity( + query, + token.parententity, + raiseerr) + if not entity: + return [], [] + path_element = entity.path_entity + mapper = entity.mapper + mappers.append(prop.parent) + key = prop.key + else: + raise sa_exc.ArgumentError("mapper option expects string key " + "or list of attributes") + + if prop is None: + return [], [] + + path = build_path(path_element, prop.key, path) + l.append(path) + if getattr(token, '_of_type', None): + path_element = mapper = token._of_type + else: + path_element = mapper = getattr(prop, 'mapper', None) + + if path_element: + path_element = path_element # if current_path tokens remain, then # we didn't have an exact path match. if current_path: return [], [] - + return l, mappers class AttributeExtension(object): |