Commits

Mike Bayer committed ef862a2

- added having() method to Query, applies HAVING to the generated statement
in the same way as filter() appends to the WHERE clause.

Comments (0)

Files changed (3)

   - 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

lib/sqlalchemy/orm/query.py

         self._statement = None
         self._params = {}
         self._criterion = None
+        self._having = None
         self._column_aggregate = None
         self._joinpoint = self.mapper
         self._aliases = None
         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``.
     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):

test/orm/query.py

         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()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.