Code

Ticket #2939: distinct-values-count-v3.patch

File distinct-values-count-v3.patch, 3.2 KB (added by dstn@…, 6 years ago)
  • django/db/models/query.py

     
    236236 
    237237        cursor = connection.cursor() 
    238238        if self._distinct: 
    239             id_col = "%s.%s" % (connection.ops.quote_name(self.model._meta.db_table), 
    240                     connection.ops.quote_name(self.model._meta.pk.column)) 
    241             cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params) 
     239            columns = ', '.join(self._get_distinct_columns()) 
     240            cursor.execute("SELECT COUNT(DISTINCT(%s))" % columns + sql, params) 
    242241        else: 
    243242            cursor.execute("SELECT COUNT(*)" + sql, params) 
    244243        count = cursor.fetchone()[0] 
     
    252251 
    253252        return count 
    254253 
     254    def _get_distinct_columns(self): 
     255        "Returns distinct columns, used for count()" 
     256        return ["%s.%s" % (connection.ops.quote_name(self.model._meta.db_table), 
     257                           connection.ops.quote_name(self.model._meta.pk.column))] 
     258 
    255259    def get(self, *args, **kwargs): 
    256260        "Performs the SELECT and returns a single object matching the given keyword arguments." 
    257261        clone = self.filter(*args, **kwargs) 
     
    580584        # select_related isn't supported in values(). 
    581585        self._select_related = False 
    582586 
     587    def _get_distinct_columns(self): 
     588        "Returns distinct columns, used for count()" 
     589        if not self._fields: 
     590            return super(ValuesQuerySet, self)._get_distinct_columns() 
     591        columns = [] 
     592        for f in self._fields: 
     593            if f in self._select: 
     594                columns.append(self._select[f]) 
     595            else: #f in [self.model._meta.fields]: 
     596                columns.append('%s.%s' % 
     597                               (connection.ops.quote_name(self.model._meta.db_table), 
     598                                connection.ops.quote_name(self.model._meta.get_field(f, many_to_many=False).column))) 
     599        return columns 
     600     
    583601    def iterator(self): 
    584602        try: 
     603            if self._distinct: 
     604                self._order_by = [] 
    585605            select, sql, params = self._get_sql_clause() 
    586606        except EmptyResultSet: 
    587607            raise StopIteration 
  • tests/modeltests/lookup/models.py

     
    168168>>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}] 
    169169True 
    170170 
     171# .values(*fields).distinct().count() will return the correct number of 
     172# distinct records. 
     173>>> Article.objects.values('pub_date').count() 
     1747 
     175>>> len(Article.objects.values('pub_date').distinct()) 
     1765 
     177>>> Article.objects.values('pub_date').distinct().count() 
     1785 
     179 
     180# And make sure it still works if no fields are passed: 
     181>>> len(Article.objects.values().distinct()) 
     1827 
     183>>> Article.objects.values().distinct().count() 
     1847 
     185 
    171186# Every DateField and DateTimeField creates get_next_by_FOO() and 
    172187# get_previous_by_FOO() methods. 
    173188# In the case of identical date values, these methods will use the ID as a