From f4ee7a1bcba5606e320c5d1b348c2d457bacec31 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 13 Nov 2007 23:07:01 +0000 Subject: [PATCH] - added having() method to Query, applies HAVING to the generated statement in the same way as filter() appends to the WHERE clause. --- CHANGES | 3 +++ lib/sqlalchemy/orm/query.py | 25 +++++++++++++++++++++++-- test/orm/query.py | 5 +++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 282079b8b2..d2a82953eb 100644 --- a/CHANGES +++ b/CHANGES @@ -83,6 +83,9 @@ CHANGES - the "properties" accessor on Mapper is removed; it now throws an informative exception explaining the usage of mapper.get_property() and mapper.iterate_properties + + - added having() method to Query, applies HAVING to the generated statement + in the same way as filter() appends to the WHERE clause. - The behavior of query.options() is now fully based on paths, i.e. an option such as eagerload_all('x.y.z.y.x') will apply eagerloading to diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index a2f8973ed5..4052e94051 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -37,6 +37,7 @@ class Query(object): self._statement = None self._params = {} self._criterion = None + self._having = None self._column_aggregate = None self._joinpoint = self.mapper self._aliases = None @@ -466,7 +467,27 @@ class Query(object): else: q._group_by = q._group_by + util.to_list(criterion) return q - + + def having(self, criterion): + """apply a HAVING criterion to the quer and return the newly resulting ``Query``.""" + + if isinstance(criterion, basestring): + criterion = sql.text(criterion) + + if criterion is not None and not isinstance(criterion, sql.ClauseElement): + raise exceptions.ArgumentError("having() argument must be of type sqlalchemy.sql.ClauseElement or string") + + + if self._aliases is not None: + criterion = self._aliases.adapt_clause(criterion) + + q = self._clone() + if q._having is not None: + q._having = q._having & criterion + else: + q._having = criterion + return q + def join(self, prop, id=None, aliased=False, from_joinpoint=False): """create a join of this ``Query`` object's criterion to a relationship and return the newly resulting ``Query``. @@ -918,7 +939,7 @@ class Query(object): def _select_args(self): """Return a dictionary of attributes that can be applied to a ``sql.Select`` statement. """ - return {'limit':self._limit, 'offset':self._offset, 'distinct':self._distinct, 'group_by':self._group_by or None} + return {'limit':self._limit, 'offset':self._offset, 'distinct':self._distinct, 'group_by':self._group_by or None, 'having':self._having or None} def _get_entity_clauses(self, m): diff --git a/test/orm/query.py b/test/orm/query.py index e736d49e4b..4f19c2c32a 100644 --- a/test/orm/query.py +++ b/test/orm/query.py @@ -349,7 +349,12 @@ class AggregateTest(QueryTest): sess = create_session() assert sess.query(Order).apply_sum(Order.user_id * Order.address_id).filter(Order.id.in_([2, 3, 4])).one() == 79 + def test_having(self): + sess = create_session() + assert [User(name=u'ed',id=8)] == sess.query(User).group_by([c for c in User.c]).join('addresses').having(func.count(Address.c.id)> 2).all() + assert [User(name=u'jack',id=7), User(name=u'fred',id=9)] == sess.query(User).group_by([c for c in User.c]).join('addresses').having(func.count(Address.c.id)< 2).all() + class CountTest(QueryTest): def test_basic(self): assert 4 == create_session().query(User).count() -- 2.47.2