Ticket #3566: aggregate.diff

File aggregate.diff, 4.9 KB (added by Nicolas Lara <nicolaslara@…>, 7 years ago)

Patch that adds aggregate functionality (no annotate yet)

  • django/db/models/sql/query.py

     
    5555        self.start_meta = None
    5656
    5757        # SQL-related attributes
     58        self.aggregates = []
    5859        self.select = []
    5960        self.tables = []    # Aliases in the order they are created.
    6061        self.where = where()
     
    140141        obj.standard_ordering = self.standard_ordering
    141142        obj.start_meta = self.start_meta
    142143        obj.select = self.select[:]
     144        obj.aggregates = self.aggregates[:]
    143145        obj.tables = self.tables[:]
    144146        obj.where = deepcopy(self.where)
    145147        obj.where_class = self.where_class
     
    173175                    row = self.resolve_columns(row, fields)
    174176                yield row
    175177
     178    def get_aggregation(self):
     179        for field in self.select:
     180            self.group_by.append(field)
     181        self.select.extend(self.aggregates)
     182        self.aggregates = []
     183        #print self.as_sql()
     184        #print 'after', self.select
     185
     186        get_name = lambda x : isinstance(x, tuple) and x[1] or x.aliased_name
     187
     188        print 'final query', self.as_sql()
     189
     190        if self.group_by:
     191            data = self.execute_sql(MULTI)
     192            result = []
     193            for rs in data.next():
     194                result.append(dict(zip([get_name(i) for i in self.select], rs)))
     195        else:
     196            data = self.execute_sql(SINGLE)
     197            result = dict(zip([get_name(i) for i in self.select], data))
     198
     199        self.select = []
     200        return result
     201
    176202    def get_count(self):
    177203        """
    178204        Performs a COUNT() query using the current filter constraints.
     
    808834            self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
    809835                    used, next, restricted)
    810836
     837    def add_aggregate(self, aggregate_expr, aliased_name, model):
     838        """
     839        Adds a single aggregate expression to the Query
     840        """
     841       
     842        field_list = aggregate_expr.split(LOOKUP_SEP)
     843        opts = model._meta
     844
     845        aggregate_func = field_list.pop()
     846       
     847        if len(field_list) > 1:
     848            field, target, opts, join_list, last = self.setup_joins(
     849                field_list, opts, self.get_initial_alias(), False)
     850            final = len(join_list)
     851            penultimate = last.pop()
     852            if penultimate == final:
     853                penultimate = last.pop()
     854            if len(join_list) > 1:
     855                extra = join_list[penultimate:]
     856                final = penultimate
     857                col = self.alias_map[extra[0]][LHS_JOIN_COL]
     858            else:
     859                col = target.column
     860               
     861            field_name = field_list.pop()
     862            alias = join_list[-1]
     863            alias = extra[final]
     864        else:
     865            field_name = field_list[0]
     866            alias = opts.db_table
     867
     868        class AggregateNode:
     869            def __init__(self, field_name, aggregate_func, aliased_name, alias):
     870                self.field_name = field_name
     871                self.aggregate_func = aggregate_func
     872                self.aliased_name = aliased_name
     873                self.alias = alias
     874               
     875            def as_sql(self, quote_func=None):
     876                if not quote_func:
     877                    quote_func = lambda x: x
     878                return '%s(%s.%s)' % (self.aggregate_func.upper(),
     879                                      quote_func(self.alias),
     880                                      quote_func(self.field_name))
     881
     882        self.aggregates.append(AggregateNode(field_name, aggregate_func, aliased_name, alias))
     883       
    811884    def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
    812885            single_filter=False):
    813886        """
  • django/db/models/query.py

     
    165165                setattr(obj, k, row[i])
    166166            yield obj
    167167
     168    def aggregate(self, *args, **kwargs):
     169        """
     170        Returns the aggregation over the current model as values (or so it should).
     171        """
     172        if args:
     173            TypeError('Unexpected positional arguments')
     174           
     175        for (aggregate_expr, alias) in kwargs.items():
     176            self.query.add_aggregate(aggregate_expr, alias, self.model)
     177        return self.query.get_aggregation()
     178
    168179    def count(self):
    169180        """
    170181        Performs a SELECT COUNT() and returns the number of records as an
     
    342353        Returns a new QuerySet that is a copy of the current one. This allows a
    343354        QuerySet to proxy for a model manager in some cases.
    344355        """
    345         return self._clone()
     356        return self._clone()       
    346357
    347358    def filter(self, *args, **kwargs):
    348359        """
Back to Top