Django backend adapter for AG Grid's Server-Side Row Model (SSRM)
Project description
django-aggrid-ssrm
A Django backend adapter for AG Grid's Server-Side Row Model (SSRM).
Translate filterModel, sortModel, pagination, search, and grouping
requests from AG Grid Enterprise into Django ORM operations with a few
lines of FieldDef config.
⚠ Important — License notice
AG Grid's Server-Side Row Model is an AG Grid Enterprise feature. You must hold a valid AG Grid Enterprise license to use SSRM in production. Without one, the grid still works but shows a trial watermark.
- AG Grid Licensing: https://www.ag-grid.com/license-pricing/
- AG Grid Enterprise EULA: https://www.ag-grid.com/eula/AG-Grid-Enterprise-License-Latest.pdf
This Django library is independent of AG Grid Ltd. It is released under the MIT License (see LICENSE) — free and open source. It implements the server-side of the SSRM protocol only; it does not bundle or redistribute the AG Grid JavaScript library itself.
Install
uv add django-aggrid-ssrm
Add the app to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
'aggrid_ssrm',
]
That's it — there are no models or migrations to apply.
Quick start
A complete SSRM endpoint in ~20 lines:
# views.py
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from aggrid_ssrm import FieldDef, SSRMConfig, SSRMRequest, process_ssrm_request
from .models import AthleteEvent
CONFIG = SSRMConfig(
fields=[
FieldDef('name', 'name', field_type='text'),
FieldDef('noc', 'noc', field_type='set'), # country code
FieldDef('year', 'year', field_type='number'),
FieldDef('sport', 'sport', field_type='set'),
FieldDef('medal', 'medal', field_type='set'),
],
default_sort=['-year', 'name'],
search_fields=['name', 'team', 'sport', 'event'],
)
@csrf_exempt
def athletes_ssrm(request):
body = json.loads(request.body or b'{}')
req = SSRMRequest.from_body(body)
result = process_ssrm_request(CONFIG, req, AthleteEvent.objects.all())
return JsonResponse(result) # {"rowData": [...], "rowCount": N}
# urls.py
urlpatterns = [
path('athletes/ssrm/', athletes_ssrm),
]
Wire AG Grid's ServerSideDatasource to POST /athletes/ssrm/ and you're done.
A full runnable example with this exact model lives in demo/.
Status / feature coverage
| Feature | Status |
|---|---|
| Filtering — text, number, date, set, blank/notBlank, combined AND/OR | Stable — extensive test coverage |
| Sorting — multi-column asc/desc | Stable |
Pagination — block loading via startRow/endRow, virtual lastRowIndex |
Stable |
| Distinct values for Set Filter dropdowns | Stable |
| Free-text search across direct + JSON-array fields | Stable |
Row expansion (one DB row → N grid rows via row_expander) |
Stable |
Row grouping & aggregation (rowGroupCols, groupKeys, valueCols) |
⚠ Experimental — see caveat below |
Pivoting (pivotMode, pivotCols) |
❌ Not implemented |
Grouping caveat
Row grouping works for many common cases and has test coverage, but is
treated as an extra / non-primary feature of this library. It depends
on the shape of your data — especially when grouping by fields inside a
JSONField, where it falls back to Python-level aggregation or raw-SQL
json_each (SQLite-flavoured). In particular:
- Multi-level grouping over JSON-array-nested fields may not match every use case; verify the row counts you get back match what you expect.
- The
json_array_configpath uses raw SQL keyed off SQLite'sjson_each/json_extract. PostgreSQL works for non-array paths, but the array-nested branch is currently SQLite-shaped. - Aggregation aliases that collide with reserved row keys
(e.g.
childCount) will be filtered out — known limitation.
If grouping doesn't fit your case, you can disable it per-column via
FieldDef(..., groupable=False), or just don't enable
enableRowGroup on the column definitions.
If you hit a grouping case that misbehaves, please open an issue with the request payload and expected vs. actual output.
Concepts
FieldDef
One per AG Grid column. Tells the engine the ORM path and what filter operators are valid.
FieldDef(
col_id='region', # AG Grid colId
orm_path='region', # Django ORM lookup path
field_type='set', # 'text' | 'number' | 'date' | 'set'
sortable=True, # default True
filterable=True, # default True
groupable=True, # default True
value_getter=None, # optional callable(instance) -> value
is_json=False, # set True for fields inside a JSONField
json_array_config=None, # for arrays nested in JSONFields
)
SSRMConfig
Per-endpoint configuration: fields, default sort, search fields, row builder, max page size.
Request flow
AG Grid → POST {filterModel, sortModel, startRow, endRow, rowGroupCols, ...}
↓
SSRMRequest.from_body(body)
↓
process_ssrm_request(config, req, queryset)
↓
apply search → apply filters
↓
grouped? ──yes──→ handle_grouped_request
no
↓
sort → paginate → row_builder
↓
{"rowData": [...], "rowCount": N}
Filter type reference
| AG Grid filter | Operator | Django lookup |
|---|---|---|
| text | contains |
__icontains |
| text | notContains |
~Q(__icontains) |
| text | equals |
__iexact |
| text | notEqual |
~Q(__iexact) |
| text | startsWith |
__istartswith |
| text | endsWith |
__iendswith |
| text | blank / notBlank |
__isnull + = '' |
| number | equals |
exact |
| number | greaterThan(OrEqual) |
__gt / __gte |
| number | lessThan(OrEqual) |
__lt / __lte |
| number | inRange |
__gte & __lte |
| number | blank / notBlank |
__isnull |
| date | equals |
__date |
| date | greaterThan |
__date__gt |
| date | lessThan |
__date__lt |
| date | inRange |
__date__gte & __date__lte |
| set | values: [...] |
__in |
| combined | AND / OR |
Q(...) & Q(...) / `Q(...) |
Class-based view helper
If you'd rather not write the JSON-decoding boilerplate yourself:
from aggrid_ssrm.views import SSRMView, SSRMColumnValuesView
class AthleteSSRMView(SSRMView):
def get_queryset(self, request):
return AthleteEvent.objects.all()
def get_config(self, request):
return CONFIG
class AthleteColumnValuesView(SSRMColumnValuesView):
def get_queryset(self, request):
return AthleteEvent.objects.all()
def get_config(self, request):
return CONFIG
Demo
A runnable Django project with an AthleteEvent model and the public
120 Years of Olympic History dataset
(271,116 rows, Athens 1896 – Rio 2016) lives in demo/:
uv sync --extra demo
uv run python demo/manage.py migrate
uv run python demo/manage.py seed_demo # all 271k rows; use --limit N for a smaller slice
uv run python demo/manage.py runserver
Open http://127.0.0.1:8000/. AG Grid Enterprise loads from a CDN; expect
the trial watermark unless you set AG_GRID_LICENSE_KEY in the
environment. See demo/README.md for the full walkthrough.
Development
uv sync --extra dev
uv run pytest
271 tests covering filters, sorting, grouping, pagination, row expansion, distinct values, JSON arrays, and edge cases.
License
This library: MIT — see LICENSE. © 2026 Zpatel.
AG Grid Enterprise (a separate product you need at runtime to use SSRM in the browser): see https://www.ag-grid.com/license-pricing/ for terms. This library does not redistribute AG Grid.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_aggrid_ssrm-0.1.0.tar.gz.
File metadata
- Download URL: django_aggrid_ssrm-0.1.0.tar.gz
- Upload date:
- Size: 19.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07f52428e1feb17f137a4f4bc1eebdd805eaf1691ba85ff5e7ddfd1432be6711
|
|
| MD5 |
e268e6904b3d3f0149f03b5821056c6b
|
|
| BLAKE2b-256 |
d156e79d86237261831fa75cd928d7493606c0b39d4a7a3f0c47f586b989f855
|
Provenance
The following attestation bundles were made for django_aggrid_ssrm-0.1.0.tar.gz:
Publisher:
publish.yml on zpatel-dev/django-aggrid-ssrm-library
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_aggrid_ssrm-0.1.0.tar.gz -
Subject digest:
07f52428e1feb17f137a4f4bc1eebdd805eaf1691ba85ff5e7ddfd1432be6711 - Sigstore transparency entry: 1595592304
- Sigstore integration time:
-
Permalink:
zpatel-dev/django-aggrid-ssrm-library@5f0267fa8e2370236b1275af59d21bad9e402a55 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/zpatel-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5f0267fa8e2370236b1275af59d21bad9e402a55 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file django_aggrid_ssrm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_aggrid_ssrm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9462d74f4ae7858ae4336f92b16cbdd7890a89bbdb8e0bbbfcf11929a24088c
|
|
| MD5 |
7146bfc8b37806f3f073d2008dce8889
|
|
| BLAKE2b-256 |
a74b73917c3c9246e18054e533c7c2c959f6d50a634fff4b069ea0f824e3529e
|
Provenance
The following attestation bundles were made for django_aggrid_ssrm-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on zpatel-dev/django-aggrid-ssrm-library
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_aggrid_ssrm-0.1.0-py3-none-any.whl -
Subject digest:
f9462d74f4ae7858ae4336f92b16cbdd7890a89bbdb8e0bbbfcf11929a24088c - Sigstore transparency entry: 1595592351
- Sigstore integration time:
-
Permalink:
zpatel-dev/django-aggrid-ssrm-library@5f0267fa8e2370236b1275af59d21bad9e402a55 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/zpatel-dev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5f0267fa8e2370236b1275af59d21bad9e402a55 -
Trigger Event:
workflow_dispatch
-
Statement type: