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

File distinct-values-count-v4.patch, 3.4 KB (added by dstn@…, 7 years ago)

Add a test and make it pass - extra() + values() + distinct() + count()

  • 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:
     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
     186# And make sure you can count() distinct() fields that are added
     187# by extra():
     188>>> Article.objects.extra(select={'poorhash':'SUBSTRING(MD5(lookup_article.headline) FROM 1 FOR 1)'}).values('poorhash').distinct().count()
     1896L
     190
    171191# Every DateField and DateTimeField creates get_next_by_FOO() and
    172192# get_previous_by_FOO() methods.
    173193# In the case of identical date values, these methods will use the ID as a
Back to Top