mirror of
https://github.com/Infinidat/infi.clickhouse_orm.git
synced 2024-11-26 02:33:45 +03:00
Added with_totals method
This commit is contained in:
parent
0c92e2ac74
commit
5f4023f120
|
@ -199,6 +199,19 @@ This queryset is translated to:
|
||||||
|
|
||||||
After calling `aggregate` you can still use most of the regular queryset methods, such as `count`, `order_by` and `paginate`. It is not possible, however, to call `only` or `aggregate`. It is also not possible to filter the queryset on calculated fields, only on fields that exist in the model.
|
After calling `aggregate` you can still use most of the regular queryset methods, such as `count`, `order_by` and `paginate`. It is not possible, however, to call `only` or `aggregate`. It is also not possible to filter the queryset on calculated fields, only on fields that exist in the model.
|
||||||
|
|
||||||
|
If you limit aggregation results, it might be useful to get total aggregation values for all rows.
|
||||||
|
To achieve this, you can use `with_totals` method. It will return extra row (last) with
|
||||||
|
values aggregated for all rows suitable for filters.
|
||||||
|
|
||||||
|
qs = Person.objects_in(database).aggregate('first_name' num='count()').with_totals().order_by('-count')[:3]
|
||||||
|
>>> print qs.count()
|
||||||
|
4
|
||||||
|
>>> for row in qs:
|
||||||
|
>>> print(row.first_name, row.count)
|
||||||
|
'Cassandra' 2
|
||||||
|
'Alexandra' 2
|
||||||
|
'' 100
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[<< Models and Databases](models_and_databases.md) | [Table of Contents](toc.md) | [Field Types >>](field_types.md)
|
[<< Models and Databases](models_and_databases.md) | [Table of Contents](toc.md) | [Field Types >>](field_types.md)
|
|
@ -287,6 +287,7 @@ class QuerySet(object):
|
||||||
self._where_q = Q()
|
self._where_q = Q()
|
||||||
self._prewhere_q = Q()
|
self._prewhere_q = Q()
|
||||||
self._grouping_fields = []
|
self._grouping_fields = []
|
||||||
|
self._grouping_with_totals = False
|
||||||
self._fields = model_cls.fields().keys()
|
self._fields = model_cls.fields().keys()
|
||||||
self._limits = None
|
self._limits = None
|
||||||
self._distinct = False
|
self._distinct = False
|
||||||
|
@ -348,6 +349,9 @@ class QuerySet(object):
|
||||||
if self._grouping_fields:
|
if self._grouping_fields:
|
||||||
sql += '\nGROUP BY %s' % comma_join('`%s`' % field for field in self._grouping_fields)
|
sql += '\nGROUP BY %s' % comma_join('`%s`' % field for field in self._grouping_fields)
|
||||||
|
|
||||||
|
if self._grouping_with_totals:
|
||||||
|
sql += ' WITH TOTALS'
|
||||||
|
|
||||||
if self._order_by:
|
if self._order_by:
|
||||||
sql += '\nORDER BY ' + self.order_by_as_sql()
|
sql += '\nORDER BY ' + self.order_by_as_sql()
|
||||||
|
|
||||||
|
@ -551,6 +555,9 @@ class AggregateQuerySet(QuerySet):
|
||||||
def select_fields_as_sql(self):
|
def select_fields_as_sql(self):
|
||||||
return comma_join(list(self._fields) + ['%s AS %s' % (v, k) for k, v in self._calculated_fields.items()])
|
return comma_join(list(self._fields) + ['%s AS %s' % (v, k) for k, v in self._calculated_fields.items()])
|
||||||
|
|
||||||
|
def group_by_as_sql(self):
|
||||||
|
return 'GROUP BY'
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self._database.select(self.as_sql()) # using an ad-hoc model
|
return self._database.select(self.as_sql()) # using an ad-hoc model
|
||||||
|
|
||||||
|
@ -561,3 +568,13 @@ class AggregateQuerySet(QuerySet):
|
||||||
sql = u'SELECT count() FROM (%s)' % self.as_sql()
|
sql = u'SELECT count() FROM (%s)' % self.as_sql()
|
||||||
raw = self._database.raw(sql)
|
raw = self._database.raw(sql)
|
||||||
return int(raw) if raw else 0
|
return int(raw) if raw else 0
|
||||||
|
|
||||||
|
def with_totals(self):
|
||||||
|
"""
|
||||||
|
Adds WITH TOTALS modifier ot GROUP BY, making query return extra row
|
||||||
|
with aggregate function calculated across all the rows. More information:
|
||||||
|
https://clickhouse.yandex/docs/en/query_language/select/#with-totals-modifier
|
||||||
|
"""
|
||||||
|
qs = copy(self)
|
||||||
|
qs._grouping_with_totals = True
|
||||||
|
return qs
|
||||||
|
|
|
@ -370,6 +370,17 @@ class AggregateTestCase(TestCaseWithData):
|
||||||
print(qs.as_sql())
|
print(qs.as_sql())
|
||||||
self.assertEqual(qs.count(), 1)
|
self.assertEqual(qs.count(), 1)
|
||||||
|
|
||||||
|
def test_aggregate_with_totals(self):
|
||||||
|
qs = Person.objects_in(self.database).aggregate('first_name', count='count()').\
|
||||||
|
with_totals().order_by('-count')[:5]
|
||||||
|
print(qs.as_sql())
|
||||||
|
result = list(qs)
|
||||||
|
self.assertEqual(len(result), 6)
|
||||||
|
for row in result[:-1]:
|
||||||
|
self.assertEqual(2, row.count)
|
||||||
|
|
||||||
|
self.assertEqual(100, result[-1].count)
|
||||||
|
|
||||||
def test_double_underscore_field(self):
|
def test_double_underscore_field(self):
|
||||||
class Mdl(Model):
|
class Mdl(Model):
|
||||||
the__number = Int32Field()
|
the__number = Int32Field()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user