web/lib/django/db/models/sql/aggregates.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     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, qn, connection):
       
    76         "Return the aggregate, rendered as SQL."
       
    77 
       
    78         if hasattr(self.col, 'as_sql'):
       
    79             field_name = self.col.as_sql(qn, connection)
       
    80         elif isinstance(self.col, (list, tuple)):
       
    81             field_name = '.'.join([qn(c) for c in self.col])
       
    82         else:
       
    83             field_name = self.col
       
    84 
       
    85         params = {
       
    86             'function': self.sql_function,
       
    87             'field': field_name
       
    88         }
       
    89         params.update(self.extra)
       
    90 
       
    91         return self.sql_template % params
       
    92 
       
    93 
       
    94 class Avg(Aggregate):
       
    95     is_computed = True
       
    96     sql_function = 'AVG'
       
    97 
       
    98 class Count(Aggregate):
       
    99     is_ordinal = True
       
   100     sql_function = 'COUNT'
       
   101     sql_template = '%(function)s(%(distinct)s%(field)s)'
       
   102 
       
   103     def __init__(self, col, distinct=False, **extra):
       
   104         super(Count, self).__init__(col, distinct=distinct and 'DISTINCT ' or '', **extra)
       
   105 
       
   106 class Max(Aggregate):
       
   107     sql_function = 'MAX'
       
   108 
       
   109 class Min(Aggregate):
       
   110     sql_function = 'MIN'
       
   111 
       
   112 class StdDev(Aggregate):
       
   113     is_computed = True
       
   114 
       
   115     def __init__(self, col, sample=False, **extra):
       
   116         super(StdDev, self).__init__(col, **extra)
       
   117         self.sql_function = sample and 'STDDEV_SAMP' or 'STDDEV_POP'
       
   118 
       
   119 class Sum(Aggregate):
       
   120     sql_function = 'SUM'
       
   121 
       
   122 class Variance(Aggregate):
       
   123     is_computed = True
       
   124 
       
   125     def __init__(self, col, sample=False, **extra):
       
   126         super(Variance, self).__init__(col, **extra)
       
   127         self.sql_function = sample and 'VAR_SAMP' or 'VAR_POP'