]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- added having() method to Query, applies HAVING to the generated statement
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 13 Nov 2007 23:07:01 +0000 (23:07 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 13 Nov 2007 23:07:01 +0000 (23:07 +0000)
    in the same way as filter() appends to the WHERE clause.

CHANGES
lib/sqlalchemy/orm/query.py
test/orm/query.py

diff --git a/CHANGES b/CHANGES
index 282079b8b286d5977aa5bfaaf1a38002464807ce..d2a82953eb6d06cb8ace90efc33ad9aadb78a544 100644 (file)
--- 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
index a2f8973ed5177e80ba1f99393762e6536f867190..4052e94051efc56bd01af3056d44cba612deca58 100644 (file)
@@ -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):
index e736d49e4be1fd54ef3b79d89161e89f9915f4fa..4f19c2c32a8b96d4490c8f4034244c6c2e7f641d 100644 (file)
@@ -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()