diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-11-02 22:11:40 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-11-02 22:11:40 +0000 |
| commit | 50719c0bb0bb2db51c0fe2024cb03d4b41ae650b (patch) | |
| tree | e87717599c3edb90e3e18e0e74782cf4f5b1c6ce | |
| parent | ff2f799ba3f5b0d1ea6d34119ff273e910db100d (diff) | |
| download | sqlalchemy-50719c0bb0bb2db51c0fe2024cb03d4b41ae650b.tar.gz | |
revert r5220 inadvertently committed to trunk
| -rw-r--r-- | lib/sqlalchemy/orm/interfaces.py | 12 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/properties.py | 67 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 18 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/util.py | 27 | ||||
| -rw-r--r-- | test/orm/inheritance/query.py | 18 | ||||
| -rw-r--r-- | test/orm/query.py | 87 | ||||
| -rwxr-xr-x | test/sql/selectable.py | 17 |
7 files changed, 82 insertions, 164 deletions
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 6a333cb2c..631d3f582 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -438,17 +438,9 @@ class PropComparator(expression.ColumnOperators): PropComparator. """ - def __init__(self, prop, mapper, adapter=None): - self.prop = self.property = prop - self.mapper = mapper - self.adapter = adapter - def __clause_element__(self): raise NotImplementedError("%r" % self) - def adapted(self, adapter): - return self.__class__(self.prop, self.mapper, adapter) - @staticmethod def any_op(a, b, **kwargs): return a.any(b, **kwargs) @@ -457,6 +449,10 @@ class PropComparator(expression.ColumnOperators): def has_op(a, b, **kwargs): return a.has(b, **kwargs) + def __init__(self, prop, mapper): + self.prop = self.property = prop + self.mapper = mapper + @staticmethod def of_type_op(a, class_): return a.of_type(class_) diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 81b048af7..87e35eb83 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -85,11 +85,8 @@ class ColumnProperty(StrategizedProperty): class ColumnComparator(PropComparator): @util.memoized_instancemethod def __clause_element__(self): - if self.adapter: - return self.adapter(self.prop.columns[0]) - else: - return self.prop.columns[0]._annotate({"parententity": self.mapper}) - + return self.prop.columns[0]._annotate({"parententity": self.mapper}) + def operate(self, op, *other, **kwargs): return op(self.__clause_element__(), *other, **kwargs) @@ -321,26 +318,18 @@ class PropertyLoader(StrategizedProperty): self._is_backref = _is_backref class Comparator(PropComparator): - def __init__(self, prop, mapper, of_type=None, adapter=None): + def __init__(self, prop, mapper, of_type=None): self.prop = self.property = prop self.mapper = mapper - self.adapter = adapter if of_type: self._of_type = _class_to_mapper(of_type) - def adapted(self, adapter): - return PropertyLoader.Comparator(self.prop, self.mapper, getattr(self, '_of_type', None), adapter) - @property def parententity(self): return self.prop.parent def __clause_element__(self): - elem = self.prop.parent._with_polymorphic_selectable - if self.adapter: - return self.adapter(elem) - else: - return elem + return self.prop.parent._with_polymorphic_selectable def operate(self, op, *other, **kwargs): return op(self, *other, **kwargs) @@ -354,15 +343,13 @@ class PropertyLoader(StrategizedProperty): def __eq__(self, other): if other is None: if self.prop.direction in [ONETOMANY, MANYTOMANY]: - return ~self._criterion_exists() + return ~sql.exists([1], self.prop.primaryjoin) else: - return self.prop._optimized_compare(None, adapt_source=self.adapter) + return self.prop._optimized_compare(None) elif self.prop.uselist: raise sa_exc.InvalidRequestError("Can't compare a collection to an object or collection; use contains() to test for membership.") else: - import pdb - pdb.set_trace() - return self.prop._optimized_compare(other, adapt_source=self.adapter) + return self.prop._optimized_compare(other) def _criterion_exists(self, criterion=None, **kwargs): if getattr(self, '_of_type', None): @@ -373,12 +360,7 @@ class PropertyLoader(StrategizedProperty): else: to_selectable = None - if self.adapter: - source_selectable = self.__clause_element__() - else: - source_selectable = None - pj, sj, source, dest, secondary, target_adapter = \ - self.prop._create_joins(dest_polymorphic=True, dest_selectable=to_selectable, source_selectable=source_selectable) + pj, sj, source, dest, secondary, target_adapter = self.prop._create_joins(dest_polymorphic=True, dest_selectable=to_selectable) for k in kwargs: crit = self.prop.mapper.class_manager.get_inst(k) == kwargs[k] @@ -387,7 +369,6 @@ class PropertyLoader(StrategizedProperty): else: criterion = criterion & crit - # why are we annotating ???? if sj: j = _orm_annotate(pj) & sj else: @@ -402,10 +383,7 @@ class PropertyLoader(StrategizedProperty): # to anything in the enclosing query. if criterion: criterion = criterion._annotate({'_halt_adapt': True}) - - crit = j & criterion - - return sql.exists([1], crit, from_obj=dest).correlate(source) + return sql.exists([1], j & criterion, from_obj=dest).correlate(source) def any(self, criterion=None, **kwargs): if not self.prop.uselist: @@ -421,7 +399,7 @@ class PropertyLoader(StrategizedProperty): def contains(self, other, **kwargs): if not self.prop.uselist: raise sa_exc.InvalidRequestError("'contains' not implemented for scalar attributes. Use ==") - clause = self.prop._optimized_compare(other, adapt_source=self.adapter) + clause = self.prop._optimized_compare(other) if self.prop.secondaryjoin: clause.negation_clause = self.__negated_contains_or_equals(other) @@ -432,22 +410,12 @@ class PropertyLoader(StrategizedProperty): if self.prop.direction == MANYTOONE: state = attributes.instance_state(other) strategy = self.prop._get_strategy(strategies.LazyLoader) - - def state_bindparam(state, col): - o = state.obj() # strong ref - return lambda: self.prop.mapper._get_committed_attr_by_column(o, col) - - def adapt(col): - if self.adapter: - return self.adapter(col) - else: - return col - if strategy.use_get: return sql.and_(*[ sql.or_( - adapt(x) != state_bindparam(state, y), - adapt(x) == None) + x != + self.prop.mapper._get_committed_state_attr_by_column(state, y), + x == None) for (x, y) in self.prop.local_remote_pairs]) criterion = sql.and_(*[x==y for (x, y) in zip(self.prop.mapper.primary_key, self.prop.mapper.primary_key_from_instance(other))]) @@ -476,11 +444,10 @@ class PropertyLoader(StrategizedProperty): else: return op(self.comparator, value) - def _optimized_compare(self, value, value_is_parent=False, adapt_source=None): + def _optimized_compare(self, value, value_is_parent=False): if value is not None: value = attributes.instance_state(value) - return self._get_strategy(strategies.LazyLoader).\ - lazy_clause(value, reverse_direction=not value_is_parent, alias_secondary=True, adapt_source=adapt_source) + return self._get_strategy(strategies.LazyLoader).lazy_clause(value, reverse_direction=not value_is_parent, alias_secondary=True) def __str__(self): return str(self.parent.class_.__name__) + "." + self.key @@ -638,10 +605,6 @@ class PropertyLoader(StrategizedProperty): "Specify a 'primaryjoin' expression. If this is a " "many-to-many relation, 'secondaryjoin' is needed as well." % (self)) - self.primaryjoin = self.primaryjoin._annotate({}) #{'_halt_adapt':True}) -# if self.secondaryjoin: -# self.secondaryjoin = self.secondaryjoin._annotate({}) #{'_halt_adapt':True}) - def _col_is_part_of_mappings(self, column): if self.secondary is None: return self.parent.mapped_table.c.contains_column(column) or \ diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index ba5541944..1962a7e2d 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -353,9 +353,9 @@ class LazyLoader(AbstractRelationLoader): self.is_class_level = True self._register_attribute(self.parent.class_, callable_=self.class_level_loader) - def lazy_clause(self, state, reverse_direction=False, alias_secondary=False, adapt_source=None): + def lazy_clause(self, state, reverse_direction=False, alias_secondary=False): if state is None: - return self._lazy_none_clause(reverse_direction, adapt_source=adapt_source) + return self._lazy_none_clause(reverse_direction) if not reverse_direction: (criterion, bind_to_col, rev) = (self.__lazywhere, self.__bind_to_col, self._equated_columns) @@ -374,12 +374,9 @@ class LazyLoader(AbstractRelationLoader): if self.parent_property.secondary and alias_secondary: criterion = sql_util.ClauseAdapter(self.parent_property.secondary.alias()).traverse(criterion) - criterion = visitors.cloned_traverse(criterion, {}, {'bindparam':visit_bindparam}) - if adapt_source: - criterion = adapt_source(criterion) - return criterion - - def _lazy_none_clause(self, reverse_direction=False, adapt_source=None): + return visitors.cloned_traverse(criterion, {}, {'bindparam':visit_bindparam}) + + def _lazy_none_clause(self, reverse_direction=False): if not reverse_direction: (criterion, bind_to_col, rev) = (self.__lazywhere, self.__bind_to_col, self._equated_columns) else: @@ -396,10 +393,7 @@ class LazyLoader(AbstractRelationLoader): binary.right = expression.null() binary.operator = operators.is_ - criterion = visitors.cloned_traverse(criterion, {}, {'binary':visit_binary}) - if adapt_source: - criterion = adapt_source(criterion) - return criterion + return visitors.cloned_traverse(criterion, {}, {'binary':visit_binary}) def class_level_loader(self, state, options=None, path=None): if not mapperutil._state_has_identity(state): diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 1c8a94669..264a4d212 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -263,12 +263,7 @@ class AliasedClass(object): def __adapt_prop(self, prop): existing = getattr(self.__target, prop.key) - - adapter = sql_util.ClauseAdapter(self.__alias, equivalents=self.__mapper._equivalent_columns, exclude=getattr(prop, 'remote_side', None)) - def adapt(elem): - return adapter.traverse(elem)._annotate({'parententity': self}) - comparator = existing.comparator.adapted(adapt) - + comparator = AliasedComparator(self, self.__adapter, existing.comparator) queryattr = attributes.QueryableAttribute( existing.impl, parententity=self, comparator=comparator) setattr(self, prop.key, queryattr) @@ -304,6 +299,22 @@ class AliasedClass(object): return '<AliasedClass at 0x%x; %s>' % ( id(self), self.__target.__name__) +class AliasedComparator(PropComparator): + def __init__(self, aliasedclass, adapter, comparator): + self.aliasedclass = aliasedclass + self.comparator = comparator + self.adapter = adapter + self.__clause_element = self.adapter.traverse(self.comparator.__clause_element__())._annotate({'parententity': aliasedclass}) + + def __clause_element__(self): + return self.__clause_element + + def operate(self, op, *other, **kwargs): + return self.adapter.traverse(self.comparator.operate(op, *other, **kwargs)) + + def reverse_operate(self, op, other, **kwargs): + return self.adapter.traverse(self.comparator.reverse_operate(op, *other, **kwargs)) + def _orm_annotate(element, exclude=None): """Deep copy the given ClauseElement, annotating each element with the "_orm_adapt" flag. @@ -313,8 +324,8 @@ def _orm_annotate(element, exclude=None): def clone(elem): if exclude and elem in exclude: elem = elem._clone() - elif '_orm_adapt' not in elem._annotations or '_halt_adapt' in elem._annotations: - elem = elem._annotate({'_orm_adapt':True, '_halt_adapt':False}) + elif '_orm_adapt' not in elem._annotations: + elem = elem._annotate({'_orm_adapt':True}) elem._copy_internals(clone=clone) return elem diff --git a/test/orm/inheritance/query.py b/test/orm/inheritance/query.py index fdeb45e3a..eb40f01e5 100644 --- a/test/orm/inheritance/query.py +++ b/test/orm/inheritance/query.py @@ -258,24 +258,6 @@ def make_test(select_type): def test_polymorphic_any(self): sess = create_session() - # test that the aliasing on "Person" does not bleed into the - # EXISTS clause generated by any() - - # fails with this line, which simulates the usage of declarative - # need to establish whether or not primaryjoin is _orm_annotated or not - # in general - #Company.employees.property.primaryjoin = Company.company_id==Person.company_id - #self.assertEquals( - # sess.query(Company).join(Company.employees, aliased=True).filter(Person.name=='dilbert').\ - # filter(Company.employees.any(Person.name=='wally')).all(), [c1] - #) - - self.assertEquals( - sess.query(Company).join(Company.employees, aliased=True).filter(Person.name=='dilbert').\ - filter(Company.employees.any(Person.name=='vlad')).all(), [] - ) - return - self.assertEquals( sess.query(Company).filter(Company.employees.of_type(Engineer).any(Engineer.primary_language=='cobol')).one(), c2 diff --git a/test/orm/query.py b/test/orm/query.py index b312c4db6..407757c10 100644 --- a/test/orm/query.py +++ b/test/orm/query.py @@ -41,9 +41,6 @@ class QueryTest(FixtureTest): }) mapper(Keyword, keywords) - compile_mappers() - #class_mapper(User).add_property('addresses', relation(Address, primaryjoin=User.id==Address.user_id, order_by=Address.id, backref='user')) - class UnicodeSchemaTest(QueryTest): keep_mappers = False @@ -360,10 +357,10 @@ class OperatorTest(QueryTest, AssertsCompiledSQL): ) # fails, needs autoaliasing - self._test( - Node.children==None, - "NOT (EXISTS (SELECT 1 FROM nodes AS nodes_1 WHERE nodes.id = nodes_1.parent_id))" - ) + #self._test( + # Node.children==None, + # "NOT (EXISTS (SELECT 1 FROM nodes AS nodes_1 WHERE nodes.id = nodes_1.parent_id))" + #) self._test( Node.parent==None, @@ -375,19 +372,33 @@ class OperatorTest(QueryTest, AssertsCompiledSQL): "nodes_1.parent_id IS NULL" ) + # fails, needs autoaliasing + #self._test( + # Node.children==[Node(id=1), Node(id=2)], + # "(EXISTS (SELECT 1 FROM nodes AS nodes_1 WHERE nodes.id = nodes_1.parent_id AND nodes_1.id = :id_1)) " + # "AND (EXISTS (SELECT 1 FROM nodes AS nodes_1 WHERE nodes.id = nodes_1.parent_id AND nodes_1.id = :id_2))" + #) + # fails, overaliases - self._test( - nalias.children==None, - "NOT (EXISTS (SELECT 1 FROM nodes WHERE nodes_1.id = nodes.parent_id))" - ) + #self._test( + # nalias.children==[Node(id=1), Node(id=2)], + # "(EXISTS (SELECT 1 FROM nodes AS nodes_1 WHERE nodes.id = nodes_1.parent_id AND nodes_1.id = :id_1)) " + # "AND (EXISTS (SELECT 1 FROM nodes AS nodes_1 WHERE nodes.id = nodes_1.parent_id AND nodes_1.id = :id_2))" + #) + + # fails, overaliases + #self._test( + # nalias.children==None, + # "NOT (EXISTS (SELECT 1 FROM nodes AS nodes WHERE nodes_1.id = nodes.parent_id))" + #) # fails - self._test( - nalias.children.any(Node.data=='some data'), - "EXISTS (SELECT 1 FROM nodes WHERE " - "nodes_1.id = nodes.parent_id AND nodes.data = :data_1)") + #self._test( + # nalias.children.any(Node.data=='some data'), + # "EXISTS (SELECT 1 FROM nodes WHERE " + # "nodes_1.id = nodes.parent_id AND nodes.data = :data_1)") - # fails, but I think I want this to fail + # fails #self._test( # Node.children.any(nalias.data=='some data'), # "EXISTS (SELECT 1 FROM nodes AS nodes_1 WHERE " @@ -395,10 +406,10 @@ class OperatorTest(QueryTest, AssertsCompiledSQL): # ) # fails, overaliases - self._test( - nalias.parent.has(Node.data=='some data'), - "EXISTS (SELECT 1 FROM nodes WHERE nodes.id = nodes_1.parent_id AND nodes.data = :data_1)" - ) + #self._test( + # nalias.parent.has(Node.data=='some data'), + # "EXISTS (SELECT 1 FROM nodes WHERE nodes.id = nodes_1.parent_id AND nodes.data = :data_1)" + #) self._test( Node.parent.has(Node.data=='some data'), @@ -417,10 +428,10 @@ class OperatorTest(QueryTest, AssertsCompiledSQL): # fails # (also why are we doing an EXISTS for this??) - self._test( - nalias.parent != Node(id=7), - 'nodes_1.parent_id != :parent_id_1 OR nodes_1.parent_id IS NULL' - ) + #self._test( + # nalias.parent != Node(id=7), + # 'NOT (EXISTS (SELECT 1 FROM nodes WHERE nodes.id = nodes_1.parent_id AND nodes.id = :id_1))' + #) self._test( nalias.children.contains(Node(id=7)), "nodes_1.id = :param_1" @@ -440,7 +451,8 @@ class OperatorTest(QueryTest, AssertsCompiledSQL): def test_selfref_between(self): ualias = aliased(User) self._test(User.id.between(ualias.id, ualias.id), "users.id BETWEEN users_1.id AND users_1.id") - self._test(ualias.id.between(User.id, User.id), "users_1.id BETWEEN users.id AND users.id") + # fails: + # self._test(ualias.id.between(User.id, User.id), "users_1.id BETWEEN users.id AND users.id") def test_clauses(self): for (expr, compare) in ( @@ -557,31 +569,6 @@ class TextTest(QueryTest): def test_binds(self): assert [User(id=8), User(id=9)] == create_session().query(User).filter("id in (:id1, :id2)").params(id1=8, id2=9).all() - -class FooTest(FixtureTest): - keep_data = True - - def test_filter_by(self): - clear_mappers() - sess = create_session(bind=testing.db) - from sqlalchemy.ext.declarative import declarative_base - Base = declarative_base(bind=testing.db) - class User(Base, _base.ComparableEntity): - __table__ = users - - class Address(Base, _base.ComparableEntity): - __table__ = addresses - - compile_mappers() -# Address.user = relation(User, primaryjoin="User.id==Address.user_id") - Address.user = relation(User, primaryjoin=User.id==Address.user_id) -# Address.user = relation(User, primaryjoin=users.c.id==addresses.c.user_id) - compile_mappers() -# Address.user.property.primaryjoin = User.id==Address.user_id - user = sess.query(User).get(8) - print sess.query(Address).filter_by(user=user).all() - assert [Address(id=2), Address(id=3), Address(id=4)] == sess.query(Address).filter_by(user=user).all() - class FilterTest(QueryTest): def test_basic(self): assert [User(id=7), User(id=8), User(id=9),User(id=10)] == create_session().query(User).all() diff --git a/test/sql/selectable.py b/test/sql/selectable.py index f6bf3eb08..e41165b5b 100755 --- a/test/sql/selectable.py +++ b/test/sql/selectable.py @@ -5,7 +5,7 @@ every selectable unit behaving nicely with others..""" import testenv; testenv.configure_for_tests() from sqlalchemy import * from testlib import * -from sqlalchemy.sql import util as sql_util, visitors +from sqlalchemy.sql import util as sql_util from sqlalchemy import exc metadata = MetaData() @@ -475,21 +475,6 @@ class AnnotationsTest(TestBase): assert inner.corresponding_column(t2.c.col1, require_embedded=False) is inner.corresponding_column(t2.c.col1, require_embedded=True) is inner.c.col1 assert inner.corresponding_column(t1.c.col1, require_embedded=False) is inner.corresponding_column(t1.c.col1, require_embedded=True) is inner.c.col1 - def test_annotated_visit(self): - from sqlalchemy.sql import table, column - table1 = table('table1', column("col1"), column("col2")) - - bin = table1.c.col1 == bindparam('foo', value=None) - assert str(bin) == "table1.col1 = :foo" - def visit_binary(b): - b.right = table1.c.col2 - b2 = visitors.cloned_traverse(bin, {}, {'binary':visit_binary}) - assert str(b2) == "table1.col1 = table1.col2" - - b3 = visitors.cloned_traverse(bin._annotate({}), {}, {'binary':visit_binary}) - assert str(b3) == "table1.col1 = table1.col2" - - if __name__ == "__main__": testenv.main() |
