diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-04-17 18:04:37 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-04-17 18:04:37 +0000 |
| commit | d3b71149376468dc8cc6e869bf9acfa67b84a872 (patch) | |
| tree | db9f5c8aac26534673a2362fd64a673c13527fc7 /lib/sqlalchemy | |
| parent | b81a0132a84a54559b7dbce075988dec62ed7da7 (diff) | |
| download | sqlalchemy-d3b71149376468dc8cc6e869bf9acfa67b84a872.tar.gz | |
- added query.with_parent(someinstance) method. searches for
target instance using lazy join criterion from parent instance.
takes optional string "property" to isolate the desired relation.
also adds static Query.query_from_parent(instance, property)
version. [ticket:541]
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/query.py | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index f61e477b6..fe7595b9d 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -5,7 +5,7 @@ # the MIT License: http://www.opensource.org/licenses/mit-license.php from sqlalchemy import sql, util, exceptions, sql_util, logging, schema -from sqlalchemy.orm import mapper, class_mapper +from sqlalchemy.orm import mapper, class_mapper, object_mapper from sqlalchemy.orm.interfaces import OperationContext, SynonymProperty __all__ = ['Query', 'QueryContext', 'SelectionContext'] @@ -348,6 +348,79 @@ class Query(object): t = sql.text(text) return self.execute(t, params=params) + def _with_parent_criterion(cls, mapper, instance, prop): + """extract query criterion from a LazyLoader strategy given a Mapper, + source persisted/detached instance and PropertyLoader. + + .""" + from sqlalchemy.orm import strategies + # this could be done very slickly with prop.compare() and join_via(), + # but we are using the less generic LazyLoader clause so that we + # retain the ability to do a self-referential join (also the lazy + # clause typically requires only the primary table with no JOIN) + loader = prop._get_strategy(strategies.LazyLoader) + criterion = loader.lazywhere.copy_container() + bind_to_col = dict([(loader.lazybinds[col].key, col) for col in loader.lazybinds]) + + class Visitor(sql.ClauseVisitor): + def visit_bindparam(self, bindparam): + bindparam.value = mapper.get_attr_by_column(instance, bind_to_col[bindparam.key]) + Visitor().traverse(criterion) + return criterion + _with_parent_criterion = classmethod(_with_parent_criterion) + + def query_from_parent(cls, instance, property, **kwargs): + """return a newly constructed Query object, with criterion corresponding to + a relationship to the given parent instance. + + instance + a persistent or detached instance which is related to class represented + by this query. + + property + string name of the property which relates this query's class to the + instance. + + \**kwargs + all extra keyword arguments are propigated to the constructor of + Query. + + """ + + mapper = object_mapper(instance) + prop = mapper.props[property] + target = prop.mapper + criterion = cls._with_parent_criterion(mapper, instance, prop) + return Query(target, **kwargs).filter(criterion) + query_from_parent = classmethod(query_from_parent) + + def with_parent(self, instance, property=None): + """add a join criterion corresponding to a relationship to the given parent instance. + + instance + a persistent or detached instance which is related to class represented + by this query. + + property + string name of the property which relates this query's class to the + instance. if None, the method will attempt to find a suitable property. + + currently, this method only works with immediate parent relationships, but in the + future may be enhanced to work across a chain of parent mappers. + """ + + from sqlalchemy.orm import properties + mapper = object_mapper(instance) + if property is None: + for prop in mapper.props.values(): + if isinstance(prop, properties.PropertyLoader) and prop.mapper is self.mapper: + break + else: + raise exceptions.InvalidRequestError("Could not locate a property which relates instances of class '%s' to instances of class '%s'" % (self.mapper.class_.__name__, instance.__class__.__name__)) + else: + prop = mapper.props[property] + return self.filter(Query._with_parent_criterion(mapper, instance, prop)) + def add_entity(self, entity): """add a mapped entity to the list of result columns to be returned. |
