Django

Code

Ticket #3566: aggregate.diff

File aggregate.diff, 4.9 kB (added by Nicolas Lara <nicolaslara@gmail.com>, 4 months ago)

Patch that adds aggregate functionality (no annotate yet)

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

    old new  
    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

    old new  
    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        """