strawberry-django-aggregates
Hasura-shape aggregations over Django querysets in Strawberry GraphQL.
count · count_distinct · sum · avg · min · max · stddev · variance · bool_and · bool_or · array_agg · string_agg — composed with multi-level group_by, having filters, and ordering on aggregate aliases. Inspired by Hasura's <table>_aggregate, PostGraphile's pg-aggregates, and Odoo 18's _read_group. Built for strawberry-django over PostgreSQL and SQLite.
from decimal import Decimal
from datetime import datetime
from strawberry import auto
import strawberry, strawberry_django
from strawberry_django_aggregates import AggregateBuilder
from .models import Order
@strawberry_django.type(Order)
class OrderType:
id: auto
customer: "CustomerType"
total: Decimal
status: str
created_at: datetime
# One call wires count/sum/avg/min/max + group_by + having into the schema:
order_aggs = AggregateBuilder(
model=Order,
aggregate_fields=["total"],
group_by_fields=["customer", "status", "created_at"],
).build()
@strawberry.type
class Query:
orders_aggregate = order_aggs.aggregate_field
orders_group_by = order_aggs.group_by_field
Generates a fully-typed GraphQL surface:
type Query {
ordersAggregate(filter: OrderFilter): OrderAggregate!
ordersGroupBy(
filter: OrderFilter
groupBy: [OrderGroupBySpec!]!
having: OrderHaving
orderBy: [OrderGroupOrder!]
pagination: OffsetPagination
): OrderGroupedResult!
}
type OrderAggregate {
count: Int!
countDistinct(field: OrderCountableField!): Int!
sum: OrderSumFields
avg: OrderAvgFields
min: OrderMinFields
max: OrderMaxFields
stddev: OrderStddevFields # Postgres only
variance: OrderVarianceFields # Postgres only
}
type OrderGrouped {
key: OrderGroupKey! # composite — every requested groupBy field present
count: Int!
sum: OrderSumFields
# ... no recursive subgroups field — flat results
}
Features
- Hasura-canonical schema shape.
<Model>Aggregate { count, countDistinct, sum, avg, min, max, stddev, variance, boolAnd, boolOr, arrayAgg, stringAgg }. - Odoo-grade group-by. Multi-level via composite keys (flat result rows), dual date-granularity tracks (
date_truncreturningDateTimeANDdate_part::intreturningInt), timezone-correct bucketing. - HAVING with aggregate aliases.
{ countGt: 5, sumTotalGt: 1000 }— typed inputs generated per measure. - Ordering on aggregates.
[{ field: "total:sum", direction: DESC }]— fail-loud on unknown terms (Odoo's pre-17 silent-drop bug avoided). - Standalone backend primitive.
compute_aggregation(qs, group_by, aggregates, having, order_by, ...)is callable from any Python context — DRF view, Celery task, admin script, MCP tool — not just GraphQL resolvers. - Composable group-key projection. A custom grouped envelope can pass the
same
group_by_expressionsmapping tocompute_aggregationandAggregateBuilder.count_groupsfor selected to-one scalar/date axes. Native aliases, temporal bucketing, HAVING, rows, and exact counts stay aligned; unsupported axes fail loud. - Exact group cardinality.
AggregateBuilder.count_groups(...)returns the database-side group count before pagination for custom grouped envelopes, including filters, granularity, JSON paths, andHAVING. - Determinism. Type generation produces byte-identical SDL for the same inputs.
- No magic. Every operator, every granularity, every type is whitelisted.
Non-goals
- Cross-database aggregation. PostgreSQL + SQLite only. SQLite degrades gracefully on
array_agg/string_agg/stddev/variance— those operators raiseOperatorNotSupportedErrorat resolver entry. - Auto-traversal of one-to-many / many-to-many for measures. This is the silent row-multiplication footgun Odoo refuses to ship; we follow.
array_aggis the explicit escape hatch. - Permission integration. The library expects a pre-scoped queryset — the caller has already applied
accessible_by(user)or equivalent. This keeps the library compatible with django-guardian, django-rules, django-rebac (when shipped), or hand-rolled permission systems.
Status
Beta (v0.10.0). The schema shape, operator vocabulary, and compute_aggregation signature are stable for early adopters, but minor-level iteration is still expected before a 1.0 stability commitment — see docs/SPEC.md § 16. Runtime: Python 3.14, Django 6.0.
Documentation
- Full contract:
docs/SPEC.md— operator catalog, granularity tracks, HAVING semantics, ordering rules, timezone handling, and the Odoo-derived footgun audit. - Naming and wire vocabulary:
docs/TERMINOLOGY.md - Contributor quality gate:
CONTRIBUTING.md
License
BSD-3-Clause.
Metadata
Release files for strawberry-django-aggregates 0.13.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| strawberry_django_aggregates-0.13.1.tar.gz | 165.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| strawberry_django_aggregates-0.13.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 272.1 kB
Release files / strawberry_django_aggregates-0.13.1.tar.gz
| Download URL | strawberry_django_aggregates-0.13.1.tar.gz |
|---|---|
| Size | 165.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83e8b9cbab8da4a5e26161b3ba7c3df1fbf5f4dee32387c9258fcf43d493b747
|
|
BLAKE2b-256 checksum How to use checksums |
f79361f9fbdb29a595130475d8b63d20eac18b16d842c647d108b0720caa3cca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / strawberry_django_aggregates-0.13.1-py3-none-any.whl
| Download URL | strawberry_django_aggregates-0.13.1-py3-none-any.whl |
|---|---|
| Size | 106.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7cad3f4b85d5ae077a931eca0295b4e750310e029b4b179e00a12d46a7ee3e46
|
|
BLAKE2b-256 checksum How to use checksums |
50a4e13f6e93c05c9d70493460572cc69cfe9e1e3d6d83c344b40bef0a3e471a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|