web/lib/django/db/models/sql/aggregates.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 """
       
     2 Classes to represent the default SQL aggregate functions
       
     3 """
       
     4 
       
     5 class AggregateField(object):
       
     6     """An internal field mockup used to identify aggregates in the
       
     7     data-conversion parts of the database backend.
       
     8     """
       
     9     def __init__(self, internal_type):
       
    10         self.internal_type = internal_type
       
    11     def get_internal_type(self):
       
    12         return self.internal_type
       
    13 
       
    14 ordinal_aggregate_field = AggregateField('IntegerField')
       
    15 computed_aggregate_field = AggregateField('FloatField')
       
    16 
       
    17 class Aggregate(object):
       
    18     """
       
    19     Default SQL Aggregate.
       
    20     """
       
    21     is_ordinal = False
       
    22     is_computed = False
       
    23     sql_template = '%(function)s(%(field)s)'
       
    24 
       
    25     def __init__(self, col, source=None, is_summary=False, **extra):
       
    26         """Instantiate an SQL aggregate
       
    27 
       
    28          * col is a column reference describing the subject field
       
    29            of the aggregate. It can be an alias, or a tuple describing
       
    30            a table and column name.
       
    31          * source is the underlying field or aggregate definition for
       
    32            the column reference. If the aggregate is not an ordinal or
       
    33            computed type, this reference is used to determine the coerced
       
    34            output type of the aggregate.
       
    35          * extra is a dictionary of additional data to provide for the
       
    36            aggregate definition
       
    37 
       
    38         Also utilizes the class variables:
       
    39          * sql_function, the name of the SQL function that implements the
       
    40            aggregate.
       
    41          * sql_template, a template string that is used to render the
       
    42            aggregate into SQL.
       
    43          * is_ordinal, a boolean indicating if the output of this aggregate
       
    44            is an integer (e.g., a count)
       
    45          * is_computed, a boolean indicating if this output of this aggregate
       
    46            is a computed float (e.g., an average), regardless of the input
       
    47            type.
       
    48 
       
    49         """
       
    50         self.col = col
       
    51         self.source = source
       
    52         self.is_summary = is_summary
       
    53         self.extra = extra
       
    54 
       
    55         # Follow the chain of aggregate sources back until you find an
       
    56         # actual field, or an aggregate that forces a particular output
       
    57         # type. This type of this field will be used to coerce values
       
    58         # retrieved from the database.
       
    59         tmp = self
       
    60 
       
    61         while tmp and isinstance(tmp, Aggregate):
       
    62             if getattr(tmp, 'is_ordinal', False):
       
    63                 tmp = ordinal_aggregate_field
       
    64             elif getattr(tmp, 'is_computed', False):
       
    65                 tmp = computed_aggregate_field
       
    66             else:
       
    67                 tmp = tmp.source
       
    68 
       
    69         self.field = tmp
       
    70 
       
    71     def relabel_aliases(self, change_map):
       
    72         if isinstance(self.col, (list, tuple)):
       
    73             self.col = (change_map.get(self.col[0], self.col[0]), self.col[1])
       
    74 
       
    75     def as_sql(self, quote_func=None):
       
    76         "Return the aggregate, rendered as SQL."
       
    77         if not quote_func:
       
    78             quote_func = lambda x: x
       
    79 
       
    80         if hasattr(self.col, 'as_sql'):
       
    81             field_name = self.col.as_sql(quote_func)
       
    82         elif isinstance(self.col, (list, tuple)):
       
    83             field_name = '.'.join([quote_func(c) for c in self.col])
       
    84         else:
       
    85             field_name = self.col
       
    86 
       
    87         params = {
       
    88             'function': self.sql_function,
       
    89             'field': field_name
       
    90         }
       
    91         params.update(self.extra)
       
    92 
       
    93         return self.sql_template % params
       
    94 
       
    95 
       
    96 class Avg(Aggregate):
       
    97     is_computed = True
       
    98     sql_function = 'AVG'
       
    99 
       
   100 class Count(Aggregate):
       
   101     is_ordinal = True
       
   102     sql_function = 'COUNT'
       
   103     sql_template = '%(function)s(%(distinct)s%(field)s)'
       
   104 
       
   105     def __init__(self, col, distinct=False, **extra):
       
   106         super(Count, self).__init__(col, distinct=distinct and 'DISTINCT ' or '', **extra)
       
   107 
       
   108 class Max(Aggregate):
       
   109     sql_function = 'MAX'
       
   110 
       
   111 class Min(Aggregate):
       
   112     sql_function = 'MIN'
       
   113 
       
   114 class StdDev(Aggregate):
       
   115     is_computed = True
       
   116 
       
   117     def __init__(self, col, sample=False, **extra):
       
   118         super(StdDev, self).__init__(col, **extra)
       
   119         self.sql_function = sample and 'STDDEV_SAMP' or 'STDDEV_POP'
       
   120 
       
   121 class Sum(Aggregate):
       
   122     sql_function = 'SUM'
       
   123 
       
   124 class Variance(Aggregate):
       
   125     is_computed = True
       
   126 
       
   127     def __init__(self, col, sample=False, **extra):
       
   128         super(Variance, self).__init__(col, **extra)
       
   129         self.sql_function = sample and 'VAR_SAMP' or 'VAR_POP'
       
   130