Index: django/db/models/manager.py
===================================================================
--- django/db/models/manager.py	(revision 3897)
+++ django/db/models/manager.py	(working copy)
@@ -51,8 +51,10 @@
     def all(self):
         return self.get_query_set()
 
-    def count(self):
-        return self.get_query_set().count()
+    def count(self,fieldname="*"):
+        """Returns the number of rows in the default case. Else returns the number of non-null entries in the column corresponding to the fieldname.
+        """
+        return self.get_query_set().count(fieldname)
 
     def dates(self, *args, **kwargs):
         return self.get_query_set().dates(*args, **kwargs)
@@ -99,6 +101,32 @@
     def values(self, *args, **kwargs):
         return self.get_query_set().values(*args, **kwargs)
 
+    # Aggregate functions (column-oriented)
+
+    def get_aggregate(self,functype,column):
+        return self.get_query_set().get_aggregate(functype,column)
+
+    def get_aggregates(self,functypes,column):
+        return self.get_query_set().get_aggregates(functypes,column)
+
+    def sum(self,fieldname):
+        return self.get_query_set().sum(fieldname)
+
+    def min(self,fieldname):
+        return self.get_query_set().min(fieldname)
+
+    def max(self,fieldname):
+        return self.get_query_set().max(fieldname)
+
+    def avg(self,fieldname):
+        return self.get_query_set().avg(fieldname)
+
+    def stddev(self,fieldname):
+        return self.get_query_set().stddev(fieldname)
+
+    def median(self,fieldname):
+        return self.get_query_set().median(fieldname)
+
 class ManagerDescriptor(object):
     # This class ensures managers aren't accessible via model instances.
     # For example, Poll.objects works, but poll_obj.objects raises AttributeError.
Index: django/db/models/query.py
===================================================================
--- django/db/models/query.py	(revision 3897)
+++ django/db/models/query.py	(working copy)
@@ -1,5 +1,6 @@
 from django.db import backend, connection, transaction
 from django.db.models.fields import DateField, FieldDoesNotExist
+from django.db.models.fields import DateField, IntegerField, FloatField, FieldDoesNotExist
 from django.db.models import signals
 from django.dispatch import dispatcher
 from django.utils.datastructures import SortedDict
@@ -157,6 +158,32 @@
         combined._filters = self._filters | other._filters
         return combined
 
+    ##############################################
+    # HELPER METHODS THAT EXAMINE FIELD METADATA #
+    ##############################################
+
+    def is_number(self, fieldname):
+        "Returns flag, True for integer. False for float. Non-float and non-integer raises either a FieldDoesNotExist or TypeError exception."
+        field = self.model._meta.get_field(fieldname)
+        # Let the FieldDoesNotExist exception propogate
+	if isinstance(field, IntegerField):
+            return True
+        if isinstance(field, FloatField):
+            return False
+        raise TypeError, "Field %s for Model %s is not an IntegerField or FloatField" % (fieldname, self.model._meta.object_name)
+
+    def is_number_or_date(self, fieldname):
+        "Returns 0 for int; 1 for float; 2 for date. Raises either a FieldDoesNotExist or TypeError exception if not an Integer, Float or Date."
+        field = self.model._meta.get_field(fieldname)
+        # Let the FieldDoesNotExist exception propogate
+        if isinstance(field, IntegerField):
+            return 0
+        if isinstance(field, FloatField):
+            return 1
+        if isinstance(field, DateField):
+            return 2
+        raise TypeError, "Field %s for Model %s is not an IntegerField, FloatField or DateField" % (fieldname, self.model._meta.object_name)
+
     ####################################
     # METHODS THAT DO DATABASE QUERIES #
     ####################################
@@ -185,8 +212,8 @@
                     setattr(obj, k[0], row[index_end+i])
                 yield obj
 
-    def count(self):
-        "Performs a SELECT COUNT() and returns the number of records as an integer."
+    def count(self,fieldname="*"):
+        "Performs a SELECT COUNT(coulmn) and returns the number of records as an integer."
         counter = self._clone()
         counter._order_by = ()
         counter._offset = None
@@ -194,14 +221,104 @@
         counter._select_related = False
         select, sql, params = counter._get_sql_clause()
         cursor = connection.cursor()
+        if fieldname == '*':
+            column = '*'
+        else:
+            column = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
+                                backend.quote_name(fieldname))
         if self._distinct:
-            id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
-                    backend.quote_name(self.model._meta.pk.column))
-            cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params)
+            if fieldname == '*':
+                column = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
+                                    backend.quote_name(self.model._meta.pk.column))
+            cursor.execute("SELECT COUNT(DISTINCT(%s))" % column + sql, params)
         else:
-            cursor.execute("SELECT COUNT(*)" + sql, params)
+            cursor.execute("SELECT COUNT(%s)" % (column) + sql, params)
         return cursor.fetchone()[0]
 
+    def get_aggregate(self,type,column):
+        "Performs the specified aggregate function on the named column."
+        agg = self._clone()
+        agg._order_by = ()
+        agg._offset = None
+        agg._limit = None
+        agg._select_related = False
+        select, sql, params = agg._get_sql_clause()
+        cursor = connection.cursor()
+        sel = "SELECT %s(%s)" % (type, column)
+        cursor.execute(sel + sql, params)
+        return cursor.fetchone()[0]
+
+    def get_aggregates(self,types,column):
+        "Performs the specified aggregate functions on the named column."
+        agg = self._clone()
+        agg._order_by = ()
+        agg._offset = None
+        agg._limit = None
+        agg._select_related = False
+        select, sql, params = agg._get_sql_clause()
+        cursor = connection.cursor()
+        sel = []
+        sel.append( "SELECT" )
+        for type in types:
+            sel.append ( "%s(%s)," % (type, column))
+        select = " ".join(sel)[:-1]
+        cursor.execute(select + sql, params)
+        return cursor.fetchone()
+
+    def sum(self, fieldname):
+        "Performs a SELECT SUM() on the specified column."
+        isInt = self.is_number(fieldname)
+        column = self.model._meta.get_field(fieldname).column
+        result = self.get_aggregate("SUM",column)
+        if isInt:
+            return int(result)
+        return result
+
+    def avg(self, fieldname):
+        "Performs a SELECT AVG() on the specified column."
+        self.is_number(fieldname)
+        column = self.model._meta.get_field(fieldname).column
+        return self.get_aggregate("AVG",column)
+
+    def stddev(self, fieldname):
+        "Performs a SELECT STDDEV() on the specified column."
+        self.is_number(fieldname)
+        column = self.model._meta.get_field(fieldname).column
+        return self.get_aggregate("STDDEV",column)
+
+    def min(self, fieldname):
+        "Performs a SELECT MIN() on the specified column."
+        self.is_number_or_date(fieldname)
+        column = self.model._meta.get_field(fieldname).column
+        return self.get_aggregate("MIN",column)
+
+    def max(self, fieldname):
+        "Performs a SELECT MAX() on the specified column."
+        self.is_number_or_date(fieldname)
+        column = self.model._meta.get_field(fieldname).column
+        return self.get_aggregate("MAX",column)
+
+    def median(self, fieldname):
+        "Returns the median value for the specified column."
+        coltype = self.is_number_or_date(fieldname)
+        column = self.model._meta.get_field(fieldname).column
+        fetcher = self._clone()
+        fetcher._order_by = (column,)
+        fetcher._offset = None
+        fetcher._limit = None
+        fetcher._select_related = False
+        select, sql, params = fetcher._get_sql_clause()
+        sel = "SELECT %s" % (column)
+        cursor = connection.cursor()
+        cursor.execute(sel + sql, params)
+        rows = cursor.fetchall()
+        midvalue = len(rows) / 2
+        if coltype == 2:
+            # returning a date
+            return str(rows[midvalue][0])
+        else:
+            return rows[midvalue][0]
+
     def get(self, *args, **kwargs):
         "Performs the SELECT and returns a single object matching the given keyword arguments."
         clone = self.filter(*args, **kwargs)
Index: docs/db-api.txt
===================================================================
--- docs/db-api.txt	(revision 3897)
+++ docs/db-api.txt	(working copy)
@@ -844,6 +844,143 @@
 
 Note ``latest()`` exists purely for convenience and readability.
 
+Aggregate Functions
+-------------------
+
+Aggregate functions perform calculations on columns. Typically
+they return a single value. They are in two groups: high_level
+and low_level.
+
+High Level Functions
+~~~~~~~~~~~~~~~~~~~~
+
+The high_level functions are sum(), min(), max(), avg(), stddev()
+and median(). Each takes a fieldname as an argument. The type of
+the field is checked for correctness as only certain datatypes are
+allowed for each of the high level functions.
+
+sum(fieldname)
+~~~~~~~~~~~~~~
+
+Returns the sum of the named field. The field must be an
+IntegerField or a FloatField. The returned value corresponds
+with the type of the column.
+
+min(fieldname), max(fieldname)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Returns the minimum or maximum value of the named field. The field
+must be an IntegerField, FloatField or DateField. The returned value
+corresponds with the type of the field. (This is a string
+representation if the field is a DateField.)
+
+avg(fieldname)
+~~~~~~~~~~~~~~
+
+Returns the average of the named field. The field must be an
+IntegerField or a FloatField. The returned value is a Float.
+
+stddev(fieldname)
+~~~~~~~~~~~~~~~~~
+
+Returns the standard deviation of the named field. The field must be an
+IntegerField or a FloatField. The returned value is a Float.
+(Not supported on sqlite3. You get an OperationError exception.)
+
+median(fieldname)
+~~~~~~~~~~~~~~~~~
+
+Returns the median value of the named field. The field
+must be an IntegerField, FloatField or DateField. The returned
+value corresponds with the type of the field. (This is a string
+representation if the column is a DateField.) Unlike the other
+functions in this group, this function does not use the DB
+supplied capabilities. It fetches all of the values of the field
+ordered by that field and returns the middle value. (If there
+are an even number of values, the second of the two middle
+values is returned.)
+
+Low Level Functions
+~~~~~~~~~~~~~~~~~~~
+
+There are two low level functions: get_aggregate() and
+get_aggregates(). They do minimal checking and allow for
+powerful queries that potentially return multiple values
+and/or combine multiple column arithmetically.
+
+The low_level functions take columnnames instead of fieldnames.
+You must do your own conversion from fieldname to columnname
+if you are taking advantage of the fieldname mapping. (By
+default fieldnames and columnnames match each other and so
+most users will not have to worry about this distinction.)
+
+get_aggregate(type,columnname)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This function supplies direct support for all database-supplied
+aggregate functions. The type parameter is the name of an aggregate
+function such as 'SUM', 'VARIANCE' or so forth limited only by
+what set of functions your particular database supports. The return
+value uses whatever type your database connonically returns. (Most
+databases return the same type as the named column, although this
+is not the case for some functions such as "avg" or "stddev" which
+always returns a Float. Also note that sqlite3 always returns a Float
+for all aggregate function.)
+
+Note that the columnname is not explicitly checked for type and
+so it is possible to combine columns arithmetically (with care!)
+as follows:
+
+Inventory.objects.get_aggregate('AVG','quantity*price')
+
+This returns the average value of the 'quantity' column multiplied
+by the 'price' column.
+
+Meals.objects.get_aggregate('MAX','price+tax+tip')
+
+This returns the highest priced meal which is calculated by the
+database by adding the 'price', the 'tax' and the 'tip' columns.
+
+(As a repeat warning: Don't forget to get the columnname from your
+fieldname if you are using fieldname mapping.)
+
+get_aggregates(types,columnname)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This function allows a single SQL operation to perform multiple
+aggregate functions. The types field is an iterable list of
+aggregate function names. The columnname is handled in the same
+manner as with the get_aggregate() function. For example:
+
+Inventory.objects.get_aggregates(['AVG','MIN','MAX'],'quantity')
+
+The results are returned in an array.
+
+Usage
+~~~~~
+
+Typical use targets all of the rows in the targeted table.
+For example:
+
+Articles.objects.sum('wordcount')
+
+However it is possible to combine the aggregate functions with
+judicious filtering. For example:
+
+Poll.objects.filter(question__contains='football').min('pub_date')
+
+Exceptions
+~~~~~~~~~~
+
+The most common exceptions encountered when using aggregate functions are:
+
+FieldDoesNotExist - the columnname is not found.
+
+TypeError - the named column uses an unsupported type.
+
+OperationError - the functype is not supported by the database.
+
+
 Field lookups
 -------------
 
