From: Mike Bayer Date: Sat, 7 Jan 2012 20:15:27 +0000 (-0500) Subject: - [feature] query.filter() accepts multiple X-Git-Tag: rel_0_7_5~63 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=74e00ed0fa3d9ade12b9076cc2e59b5ecf4886bb;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - [feature] query.filter() accepts multiple criteria which will join via AND, i.e. query.filter(x==y, z>q, ...) --- diff --git a/CHANGES b/CHANGES index 178cac3b36..e2d6e827cb 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,10 @@ CHANGES declarative_base(). Allows two or more declarative bases to share the same registry of class names. + - [feature] query.filter() accepts multiple + criteria which will join via AND, i.e. + query.filter(x==y, z>q, ...) + - Py3K - [bug] Fixed inappropriate usage of util.py3k flag and renamed it to util.py3k_warning, since diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index f48189098d..9508cb5321 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1066,33 +1066,67 @@ class Query(object): self._params.update(kwargs) @_generative(_no_statement_condition, _no_limit_offset) - def filter(self, criterion): - """apply the given filtering criterion to the query and return - the newly resulting ``Query`` + def filter(self, *criterion): + """apply the given filtering criterion to a copy + of this :class:`.Query`, using SQL expressions. - the criterion is any sql.ClauseElement applicable to the WHERE clause - of a select. + e.g.:: + + session.query(MyClass).filter(MyClass.name == 'some name') + + Multiple criteria are joined together by AND (new in 0.7.5):: + + session.query(MyClass).\\ + filter(MyClass.name == 'some name', MyClass.id > 5) + + The criterion is any SQL expression object applicable to the + WHERE clause of a select. String expressions are coerced + into SQL expression constructs via the :func:`.text` construct. + + See also: + + :meth:`.Query.filter_by` - filter on keyword expressions. """ - if isinstance(criterion, basestring): - criterion = sql.text(criterion) + for criterion in list(criterion): + if isinstance(criterion, basestring): + criterion = sql.text(criterion) - if criterion is not None and \ - not isinstance(criterion, sql.ClauseElement): - raise sa_exc.ArgumentError( - "filter() argument must be of type " - "sqlalchemy.sql.ClauseElement or string") + if criterion is not None and \ + not isinstance(criterion, sql.ClauseElement): + raise sa_exc.ArgumentError( + "filter() argument must be of type " + "sqlalchemy.sql.ClauseElement or string") - criterion = self._adapt_clause(criterion, True, True) + criterion = self._adapt_clause(criterion, True, True) - if self._criterion is not None: - self._criterion = self._criterion & criterion - else: - self._criterion = criterion + if self._criterion is not None: + self._criterion = self._criterion & criterion + else: + self._criterion = criterion def filter_by(self, **kwargs): - """apply the given filtering criterion to the query and return - the newly resulting ``Query``.""" + """apply the given filtering criterion to a copy + of this :class:`.Query`, using keyword expressions. + + e.g.:: + + session.query(MyClass).filter_by(name = 'some name') + + Multiple criteria are joined together by AND:: + + session.query(MyClass).\\ + filter_by(name = 'some name', id = 5) + + The keyword expressions are extracted from the primary + entity of the query, or the last entity that was the + target of a call to :meth:`.Query.join`. + + See also: + + :meth:`.Query.filter` - filter on SQL expressions. + + """ clauses = [_entity_descriptor(self._joinpoint_zero(), key) == value for key, value in kwargs.iteritems()] diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 9ada53bfcd..8a8ef0d4e5 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1139,6 +1139,7 @@ class FilterTest(QueryTest, AssertsCompiledSQL): eq_(sess.query(Item).filter(Item.keywords==None).order_by(Item.id).all(), [Item(id=4), Item(id=5)]) eq_(sess.query(Item).filter(Item.keywords!=None).order_by(Item.id).all(), [Item(id=1),Item(id=2), Item(id=3)]) + def test_filter_by(self): User, Address = self.classes.User, self.classes.Address @@ -1199,7 +1200,31 @@ class FilterTest(QueryTest, AssertsCompiledSQL): create_session().query(User.id).filter_by(**{}).order_by(User.id).all() ) + def test_filter_conjunctions(self): + User = self.classes.User + s = create_session() + self.assert_compile( + s.query(User).filter(User.name=="ed", User.id>5), + "SELECT users.id AS users_id, users.name " + "AS users_name FROM users WHERE users.name = " + ":name_1 AND users.id > :id_1" + ) + + self.assert_compile( + s.query(User).filter_by(name='ed', id=5), + "SELECT users.id AS users_id, users.name " + "AS users_name FROM users WHERE users.name " + "= :name_1 AND users.id = :id_1" + ) + def test_text_coerce(self): + User = self.classes.User + s = create_session() + self.assert_compile( + s.query(User).filter("name='ed'"), + "SELECT users.id AS users_id, users.name " + "AS users_name FROM users WHERE name='ed'" + ) class SetOpsTest(QueryTest, AssertsCompiledSQL): __dialect__ = 'default'