From: Mike Bayer Date: Sun, 12 Feb 2012 23:25:19 +0000 (-0500) Subject: - [feature] Added the ability to query for X-Git-Tag: rel_0_7_6~56 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=38b7cef9828acc360c6a356a8a6bbf4f85a04348;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - [feature] Added the ability to query for Table-bound column names when using query(sometable).filter_by(colname=value). [ticket:2400] --- diff --git a/CHANGES b/CHANGES index 49488c601b..c954c2de62 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,11 @@ CHANGES 0.7.6 ===== - orm + - [feature] Added the ability to query for + Table-bound column names when using + query(sometable).filter_by(colname=value). + [ticket:2400] + - [bug] Improved the "declarative reflection" example to support single-table inheritance, multiple calls to prepare(), tables that diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 9508cb5321..a00d4078a0 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -276,6 +276,7 @@ class Query(object): return self._select_from_entity or \ self._entity_zero().entity_zero + @property def _mapper_entities(self): # TODO: this is wrong, its hardcoded to "primary entity" when @@ -3086,8 +3087,9 @@ class _MapperEntity(_QueryEntity): class _ColumnEntity(_QueryEntity): """Column/expression based entity.""" - def __init__(self, query, column): + def __init__(self, query, column, namespace=None): self.expr = column + self.namespace = namespace if isinstance(column, basestring): column = sql.literal_column(column) @@ -3106,7 +3108,7 @@ class _ColumnEntity(_QueryEntity): for c in column._select_iterable: if c is column: break - _ColumnEntity(query, c) + _ColumnEntity(query, c, namespace=column) if c is not column: return @@ -3147,12 +3149,14 @@ class _ColumnEntity(_QueryEntity): if self.entities: self.entity_zero = list(self.entities)[0] + elif self.namespace is not None: + self.entity_zero = self.namespace else: self.entity_zero = None @property def entity_zero_or_selectable(self): - if self.entity_zero: + if self.entity_zero is not None: return self.entity_zero elif self.actual_froms: return list(self.actual_froms)[0] diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 0cd5b05948..6ac03d95a9 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -557,15 +557,20 @@ def _entity_descriptor(entity, key): attribute. """ - if not isinstance(entity, (AliasedClass, type)): - entity = entity.class_ + if isinstance(entity, expression.FromClause): + description = entity + entity = entity.c + elif not isinstance(entity, (AliasedClass, type)): + description = entity = entity.class_ + else: + description = entity try: return getattr(entity, key) except AttributeError: raise sa_exc.InvalidRequestError( "Entity '%s' has no property '%s'" % - (entity, key) + (description, key) ) def _orm_columns(entity): diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 24974ae7e3..451d61a678 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1155,6 +1155,32 @@ class FilterTest(QueryTest, AssertsCompiledSQL): assert [User(name='chuck')] == sess.query(User).filter_by(addresses = None).all() assert [User(name='chuck')] == sess.query(User).filter_by(addresses = null()).all() + def test_filter_by_tables(self): + users = self.tables.users + addresses = self.tables.addresses + sess = create_session() + self.assert_compile( + sess.query(users).\ + filter_by(name='ed').\ + join(addresses, users.c.id==addresses.c.user_id).\ + filter_by(email_address='ed@ed.com'), + "SELECT users.id AS users_id, users.name AS users_name " + "FROM users JOIN addresses ON users.id = addresses.user_id " + "WHERE users.name = :name_1 AND " + "addresses.email_address = :email_address_1", + checkparams={u'email_address_1': 'ed@ed.com', u'name_1': 'ed'} + ) + + def test_filter_by_no_property(self): + addresses = self.tables.addresses + sess = create_session() + assert_raises_message( + sa.exc.InvalidRequestError, + "Entity 'addresses' has no property 'name'", + sess.query(addresses).\ + filter_by, name='ed' + ) + def test_none_comparison(self): Order, User, Address = (self.classes.Order, self.classes.User,