djangorestframework-tabulator 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- djangorestframework_tabulator-0.1.0/.github/workflows/ci.yml +82 -0
- djangorestframework_tabulator-0.1.0/.gitignore +16 -0
- djangorestframework_tabulator-0.1.0/LICENSE +21 -0
- djangorestframework_tabulator-0.1.0/Makefile +40 -0
- djangorestframework_tabulator-0.1.0/PKG-INFO +186 -0
- djangorestframework_tabulator-0.1.0/README.rst +137 -0
- djangorestframework_tabulator-0.1.0/pyproject.toml +120 -0
- djangorestframework_tabulator-0.1.0/rest_framework_tabulator/__init__.py +14 -0
- djangorestframework_tabulator-0.1.0/rest_framework_tabulator/filters.py +273 -0
- djangorestframework_tabulator-0.1.0/rest_framework_tabulator/pagination.py +60 -0
- djangorestframework_tabulator-0.1.0/rest_framework_tabulator/py.typed +0 -0
- djangorestframework_tabulator-0.1.0/tests/__init__.py +2 -0
- djangorestframework_tabulator-0.1.0/tests/settings.py +24 -0
- djangorestframework_tabulator-0.1.0/tests/test_filters.py +457 -0
- djangorestframework_tabulator-0.1.0/tests/test_pagination.py +68 -0
- djangorestframework_tabulator-0.1.0/tests/testapp/__init__.py +2 -0
- djangorestframework_tabulator-0.1.0/tests/testapp/models.py +23 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
tests:
|
|
14
|
+
name: Tests
|
|
15
|
+
timeout-minutes: 10
|
|
16
|
+
runs-on: ${{ matrix.os }}
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
os: [ubuntu-latest]
|
|
21
|
+
python-version: ['3.10', '3.12', '3.14']
|
|
22
|
+
django-version: ['4.2', '5.1', '6.1']
|
|
23
|
+
exclude:
|
|
24
|
+
# Django 6.x dropped support for Python < 3.12.
|
|
25
|
+
- python-version: '3.10'
|
|
26
|
+
django-version: '6.1'
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Check out repository
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
33
|
+
uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: ${{ matrix.python-version }}
|
|
36
|
+
|
|
37
|
+
- name: Display Python version
|
|
38
|
+
run: python --version
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
# Django and the package's own dependencies (which include an
|
|
42
|
+
# unpinned djangorestframework) are resolved together, in one
|
|
43
|
+
# call — installing them separately lets pip pick the latest
|
|
44
|
+
# djangorestframework first regardless of the Django version
|
|
45
|
+
# this job is testing, which can be incompatible with it
|
|
46
|
+
# (confirmed: djangorestframework 3.18 requires django>=5.2, so
|
|
47
|
+
# forcing an older Django on top afterwards leaves a broken
|
|
48
|
+
# environment instead of the older djangorestframework pip
|
|
49
|
+
# would have picked resolving both together).
|
|
50
|
+
run: |
|
|
51
|
+
python -m pip install --upgrade pip
|
|
52
|
+
pip install -e '.[dev]' "django==${{ matrix.django-version }}.*"
|
|
53
|
+
|
|
54
|
+
- name: Run Ruff (lint)
|
|
55
|
+
run: ruff check .
|
|
56
|
+
|
|
57
|
+
- name: Run Ruff (format)
|
|
58
|
+
run: ruff format --check .
|
|
59
|
+
|
|
60
|
+
- name: Run mypy
|
|
61
|
+
run: mypy
|
|
62
|
+
|
|
63
|
+
- name: Run pytest
|
|
64
|
+
run: |
|
|
65
|
+
mkdir -p var
|
|
66
|
+
pytest -v \
|
|
67
|
+
--junitxml=var/tests-results.xml \
|
|
68
|
+
--cov=rest_framework_tabulator \
|
|
69
|
+
--cov-report=xml:var/tests-coverage.xml
|
|
70
|
+
|
|
71
|
+
- name: Upload pytest result report
|
|
72
|
+
if: failure()
|
|
73
|
+
uses: actions/upload-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: tests-results-python_${{ matrix.python-version }}-django_${{ matrix.django-version }}.xml
|
|
76
|
+
path: var/tests-results.xml
|
|
77
|
+
|
|
78
|
+
- name: Upload Coverage Report
|
|
79
|
+
uses: actions/upload-artifact@v4
|
|
80
|
+
with:
|
|
81
|
+
name: tests-coverage-python_${{ matrix.python-version }}-django_${{ matrix.django-version }}.xml
|
|
82
|
+
path: var/tests-coverage.xml
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2026 Esteban De La Fuente Rubio / Derafu <https://www.derafu.dev>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
.PHONY: install-dev lint format format-check typecheck test check build upload clean
|
|
2
|
+
|
|
3
|
+
VENV = .venv
|
|
4
|
+
VENV_READY = $(VENV)/.installed
|
|
5
|
+
|
|
6
|
+
$(VENV_READY): pyproject.toml
|
|
7
|
+
python3 -m venv $(VENV)
|
|
8
|
+
$(VENV)/bin/pip install --upgrade pip
|
|
9
|
+
$(VENV)/bin/pip install -e '.[dev]'
|
|
10
|
+
touch $(VENV_READY)
|
|
11
|
+
|
|
12
|
+
install-dev: $(VENV_READY)
|
|
13
|
+
|
|
14
|
+
lint: $(VENV_READY)
|
|
15
|
+
$(VENV)/bin/ruff check .
|
|
16
|
+
|
|
17
|
+
format: $(VENV_READY)
|
|
18
|
+
$(VENV)/bin/ruff format .
|
|
19
|
+
|
|
20
|
+
format-check: $(VENV_READY)
|
|
21
|
+
$(VENV)/bin/ruff format --check .
|
|
22
|
+
|
|
23
|
+
typecheck: $(VENV_READY)
|
|
24
|
+
$(VENV)/bin/mypy
|
|
25
|
+
|
|
26
|
+
test: $(VENV_READY)
|
|
27
|
+
$(VENV)/bin/pytest -v
|
|
28
|
+
|
|
29
|
+
check: lint format-check typecheck test
|
|
30
|
+
|
|
31
|
+
build: $(VENV_READY)
|
|
32
|
+
$(VENV)/bin/python -m build
|
|
33
|
+
|
|
34
|
+
upload: build
|
|
35
|
+
$(VENV)/bin/twine check dist/*
|
|
36
|
+
$(VENV)/bin/twine upload dist/*
|
|
37
|
+
|
|
38
|
+
clean:
|
|
39
|
+
rm -rf dist build *.egg-info .pytest_cache .ruff_cache .mypy_cache
|
|
40
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: djangorestframework-tabulator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Remote pagination and filtering for Tabulator (tabulator.info) on Django REST Framework.
|
|
5
|
+
Author: Esteban De La Fuente Rubio / Derafu
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (C) 2026 Esteban De La Fuente Rubio / Derafu <https://www.derafu.dev>
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Keywords: django,djangorestframework,filtering,pagination,tabulator
|
|
29
|
+
Classifier: Framework :: Django
|
|
30
|
+
Classifier: Framework :: Django :: 4
|
|
31
|
+
Classifier: Framework :: Django :: 5
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Requires-Python: >=3.10
|
|
36
|
+
Requires-Dist: django>=4.2
|
|
37
|
+
Requires-Dist: djangorestframework>=3.15
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: build; extra == 'dev'
|
|
40
|
+
Requires-Dist: django-stubs; extra == 'dev'
|
|
41
|
+
Requires-Dist: djangorestframework-stubs; extra == 'dev'
|
|
42
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
43
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest-django; extra == 'dev'
|
|
46
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
47
|
+
Requires-Dist: twine; extra == 'dev'
|
|
48
|
+
Description-Content-Type: text/x-rst
|
|
49
|
+
|
|
50
|
+
djangorestframework-tabulator
|
|
51
|
+
==============================
|
|
52
|
+
|
|
53
|
+
|build-status-image| |pypi-version| |py-versions|
|
|
54
|
+
|
|
55
|
+
Remote pagination and filtering for `Tabulator <https://tabulator.info>`_
|
|
56
|
+
on top of `Django REST Framework <https://www.django-rest-framework.org>`_.
|
|
57
|
+
|
|
58
|
+
Why this exists
|
|
59
|
+
----------------
|
|
60
|
+
|
|
61
|
+
Tabulator, in remote mode (``pagination: "remote"``, ``filterMode:
|
|
62
|
+
"remote"``, ``sortMode: "remote"``), sends pagination/filtering/sorting
|
|
63
|
+
using its own query string convention, and expects the response back in
|
|
64
|
+
its own shape — neither matches what DRF ships out of the box
|
|
65
|
+
(``PageNumberPagination`` responds with ``{count, next, previous,
|
|
66
|
+
results}``; its ``filter_backends`` don't understand nested bracketed
|
|
67
|
+
keys). This package translates both sides. `jQuery DataTables
|
|
68
|
+
<https://datatables.net>`_ already has a mature DRF adapter
|
|
69
|
+
(`djangorestframework-datatables
|
|
70
|
+
<https://pypi.org/project/djangorestframework-datatables/>`_); no
|
|
71
|
+
equivalent exists for Tabulator.
|
|
72
|
+
|
|
73
|
+
Usage
|
|
74
|
+
-----
|
|
75
|
+
|
|
76
|
+
.. code-block:: python
|
|
77
|
+
|
|
78
|
+
from rest_framework.generics import ListAPIView
|
|
79
|
+
from rest_framework_tabulator import TabulatorFilterBackend, TabulatorPagination
|
|
80
|
+
|
|
81
|
+
class DocumentListView(ListAPIView):
|
|
82
|
+
queryset = Document.objects.all()
|
|
83
|
+
serializer_class = DocumentSerializer
|
|
84
|
+
pagination_class = TabulatorPagination
|
|
85
|
+
filter_backends = [TabulatorFilterBackend]
|
|
86
|
+
filterset_fields = ['number', 'status', 'issued_at']
|
|
87
|
+
ordering_fields = ['issued_at', 'number', 'total']
|
|
88
|
+
|
|
89
|
+
Tabulator's contract (confirmed against the source code of
|
|
90
|
+
``tabulator-tables/tabulator`` itself, not third-party documentation):
|
|
91
|
+
|
|
92
|
+
- Request: ``page``/``size`` (pagination), ``sort[i][field]``/
|
|
93
|
+
``sort[i][dir]`` (sorting), ``filter[i][field]``/``filter[i][type]``/
|
|
94
|
+
``filter[i][value]`` (filtering) — an array value (the ``in`` type) is
|
|
95
|
+
sent as ``filter[i][value][0]``, ``filter[i][value][1]``, etc.
|
|
96
|
+
- Expected response: ``{"data": [...], "last_page": N}``.
|
|
97
|
+
|
|
98
|
+
``filterset_fields``/``ordering_fields`` on the view are allowlists of
|
|
99
|
+
fields (same attribute names ``django-filter``/``rest_framework.filters
|
|
100
|
+
.OrderingFilter`` already use, so they look familiar) — any field not
|
|
101
|
+
declared there is silently ignored, on both the filter and the sort
|
|
102
|
+
side. Leaving either one undeclared denies filtering/sorting entirely
|
|
103
|
+
rather than allowing every field by default.
|
|
104
|
+
|
|
105
|
+
Supported filter types
|
|
106
|
+
-----------------------
|
|
107
|
+
|
|
108
|
+
Every filter type Tabulator ships is supported (confirmed against
|
|
109
|
+
``Filter/defaults/filters.js`` in the Tabulator source for the exact
|
|
110
|
+
set of built-in types):
|
|
111
|
+
|
|
112
|
+
.. list-table::
|
|
113
|
+
:header-rows: 1
|
|
114
|
+
|
|
115
|
+
* - Tabulator ``type``
|
|
116
|
+
- Django lookup
|
|
117
|
+
* - ``=``
|
|
118
|
+
- exact
|
|
119
|
+
* - ``!=``
|
|
120
|
+
- negated exact
|
|
121
|
+
* - ``<``, ``<=``, ``>``, ``>=``
|
|
122
|
+
- ``lt``, ``lte``, ``gt``, ``gte``
|
|
123
|
+
* - ``like``
|
|
124
|
+
- ``icontains``
|
|
125
|
+
* - ``keywords``
|
|
126
|
+
- ``icontains`` (see note below)
|
|
127
|
+
* - ``starts``
|
|
128
|
+
- ``istartswith``
|
|
129
|
+
* - ``ends``
|
|
130
|
+
- ``iendswith``
|
|
131
|
+
* - ``regex``
|
|
132
|
+
- ``regex``
|
|
133
|
+
* - ``in``
|
|
134
|
+
- ``in`` (array value)
|
|
135
|
+
* - ``smart``
|
|
136
|
+
- ported from Tabulator's own ``smart`` filter
|
|
137
|
+
* - ``smarter``
|
|
138
|
+
- ported from Tabulator's own ``smarter`` filter
|
|
139
|
+
|
|
140
|
+
``keywords`` note: Tabulator's client-side ``keywords`` filter also
|
|
141
|
+
supports a custom word separator and an "all words must match" toggle
|
|
142
|
+
(``headerFilterFuncParams``), but neither is sent to the server in
|
|
143
|
+
remote mode — the ajax payload only ever carries ``{field, type,
|
|
144
|
+
value}``. It is approximated here as a plain substring match, same as
|
|
145
|
+
``like``.
|
|
146
|
+
|
|
147
|
+
``smart``/``smarter`` are a faithful port of Tabulator's own filter
|
|
148
|
+
functions, not a Django-specific addition: ``.`` matches any non-empty
|
|
149
|
+
value, ``!`` matches empty/null, a leading comparison operator does a
|
|
150
|
+
numeric comparison, a leading ``=`` does an exact match, multiple
|
|
151
|
+
whitespace-separated words become an ``AND`` of a substring match per
|
|
152
|
+
word, ``AND``/``OR`` combine sub-expressions left to right (no operator
|
|
153
|
+
precedence, same as the original), and anything else falls back to a
|
|
154
|
+
substring match.
|
|
155
|
+
|
|
156
|
+
A value that doesn't fit the field's type (e.g. a non-numeric string
|
|
157
|
+
against an integer field, or an invalid date) raises a DRF
|
|
158
|
+
``ValidationError`` — a clean ``400`` naming the offending field —
|
|
159
|
+
instead of an unhandled ``500``. Django validates most field types
|
|
160
|
+
eagerly, right when the filter is applied, so this is caught there
|
|
161
|
+
rather than only when the queryset is evaluated.
|
|
162
|
+
|
|
163
|
+
License
|
|
164
|
+
-------
|
|
165
|
+
|
|
166
|
+
MIT — see ``LICENSE``.
|
|
167
|
+
|
|
168
|
+
Development
|
|
169
|
+
-----------
|
|
170
|
+
|
|
171
|
+
.. code-block:: bash
|
|
172
|
+
|
|
173
|
+
pip install -e '.[dev]'
|
|
174
|
+
pytest
|
|
175
|
+
|
|
176
|
+
.. |build-status-image| image:: https://github.com/derafu/djangorestframework-tabulator/actions/workflows/ci.yml/badge.svg
|
|
177
|
+
:target: https://github.com/derafu/djangorestframework-tabulator/actions/workflows/ci.yml
|
|
178
|
+
:alt: CI
|
|
179
|
+
|
|
180
|
+
.. |pypi-version| image:: https://img.shields.io/pypi/v/djangorestframework-tabulator.svg
|
|
181
|
+
:target: https://pypi.org/project/djangorestframework-tabulator/
|
|
182
|
+
:alt: Pypi version
|
|
183
|
+
|
|
184
|
+
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/djangorestframework-tabulator.svg
|
|
185
|
+
:target: https://pypi.org/project/djangorestframework-tabulator/
|
|
186
|
+
:alt: Python versions
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
djangorestframework-tabulator
|
|
2
|
+
==============================
|
|
3
|
+
|
|
4
|
+
|build-status-image| |pypi-version| |py-versions|
|
|
5
|
+
|
|
6
|
+
Remote pagination and filtering for `Tabulator <https://tabulator.info>`_
|
|
7
|
+
on top of `Django REST Framework <https://www.django-rest-framework.org>`_.
|
|
8
|
+
|
|
9
|
+
Why this exists
|
|
10
|
+
----------------
|
|
11
|
+
|
|
12
|
+
Tabulator, in remote mode (``pagination: "remote"``, ``filterMode:
|
|
13
|
+
"remote"``, ``sortMode: "remote"``), sends pagination/filtering/sorting
|
|
14
|
+
using its own query string convention, and expects the response back in
|
|
15
|
+
its own shape — neither matches what DRF ships out of the box
|
|
16
|
+
(``PageNumberPagination`` responds with ``{count, next, previous,
|
|
17
|
+
results}``; its ``filter_backends`` don't understand nested bracketed
|
|
18
|
+
keys). This package translates both sides. `jQuery DataTables
|
|
19
|
+
<https://datatables.net>`_ already has a mature DRF adapter
|
|
20
|
+
(`djangorestframework-datatables
|
|
21
|
+
<https://pypi.org/project/djangorestframework-datatables/>`_); no
|
|
22
|
+
equivalent exists for Tabulator.
|
|
23
|
+
|
|
24
|
+
Usage
|
|
25
|
+
-----
|
|
26
|
+
|
|
27
|
+
.. code-block:: python
|
|
28
|
+
|
|
29
|
+
from rest_framework.generics import ListAPIView
|
|
30
|
+
from rest_framework_tabulator import TabulatorFilterBackend, TabulatorPagination
|
|
31
|
+
|
|
32
|
+
class DocumentListView(ListAPIView):
|
|
33
|
+
queryset = Document.objects.all()
|
|
34
|
+
serializer_class = DocumentSerializer
|
|
35
|
+
pagination_class = TabulatorPagination
|
|
36
|
+
filter_backends = [TabulatorFilterBackend]
|
|
37
|
+
filterset_fields = ['number', 'status', 'issued_at']
|
|
38
|
+
ordering_fields = ['issued_at', 'number', 'total']
|
|
39
|
+
|
|
40
|
+
Tabulator's contract (confirmed against the source code of
|
|
41
|
+
``tabulator-tables/tabulator`` itself, not third-party documentation):
|
|
42
|
+
|
|
43
|
+
- Request: ``page``/``size`` (pagination), ``sort[i][field]``/
|
|
44
|
+
``sort[i][dir]`` (sorting), ``filter[i][field]``/``filter[i][type]``/
|
|
45
|
+
``filter[i][value]`` (filtering) — an array value (the ``in`` type) is
|
|
46
|
+
sent as ``filter[i][value][0]``, ``filter[i][value][1]``, etc.
|
|
47
|
+
- Expected response: ``{"data": [...], "last_page": N}``.
|
|
48
|
+
|
|
49
|
+
``filterset_fields``/``ordering_fields`` on the view are allowlists of
|
|
50
|
+
fields (same attribute names ``django-filter``/``rest_framework.filters
|
|
51
|
+
.OrderingFilter`` already use, so they look familiar) — any field not
|
|
52
|
+
declared there is silently ignored, on both the filter and the sort
|
|
53
|
+
side. Leaving either one undeclared denies filtering/sorting entirely
|
|
54
|
+
rather than allowing every field by default.
|
|
55
|
+
|
|
56
|
+
Supported filter types
|
|
57
|
+
-----------------------
|
|
58
|
+
|
|
59
|
+
Every filter type Tabulator ships is supported (confirmed against
|
|
60
|
+
``Filter/defaults/filters.js`` in the Tabulator source for the exact
|
|
61
|
+
set of built-in types):
|
|
62
|
+
|
|
63
|
+
.. list-table::
|
|
64
|
+
:header-rows: 1
|
|
65
|
+
|
|
66
|
+
* - Tabulator ``type``
|
|
67
|
+
- Django lookup
|
|
68
|
+
* - ``=``
|
|
69
|
+
- exact
|
|
70
|
+
* - ``!=``
|
|
71
|
+
- negated exact
|
|
72
|
+
* - ``<``, ``<=``, ``>``, ``>=``
|
|
73
|
+
- ``lt``, ``lte``, ``gt``, ``gte``
|
|
74
|
+
* - ``like``
|
|
75
|
+
- ``icontains``
|
|
76
|
+
* - ``keywords``
|
|
77
|
+
- ``icontains`` (see note below)
|
|
78
|
+
* - ``starts``
|
|
79
|
+
- ``istartswith``
|
|
80
|
+
* - ``ends``
|
|
81
|
+
- ``iendswith``
|
|
82
|
+
* - ``regex``
|
|
83
|
+
- ``regex``
|
|
84
|
+
* - ``in``
|
|
85
|
+
- ``in`` (array value)
|
|
86
|
+
* - ``smart``
|
|
87
|
+
- ported from Tabulator's own ``smart`` filter
|
|
88
|
+
* - ``smarter``
|
|
89
|
+
- ported from Tabulator's own ``smarter`` filter
|
|
90
|
+
|
|
91
|
+
``keywords`` note: Tabulator's client-side ``keywords`` filter also
|
|
92
|
+
supports a custom word separator and an "all words must match" toggle
|
|
93
|
+
(``headerFilterFuncParams``), but neither is sent to the server in
|
|
94
|
+
remote mode — the ajax payload only ever carries ``{field, type,
|
|
95
|
+
value}``. It is approximated here as a plain substring match, same as
|
|
96
|
+
``like``.
|
|
97
|
+
|
|
98
|
+
``smart``/``smarter`` are a faithful port of Tabulator's own filter
|
|
99
|
+
functions, not a Django-specific addition: ``.`` matches any non-empty
|
|
100
|
+
value, ``!`` matches empty/null, a leading comparison operator does a
|
|
101
|
+
numeric comparison, a leading ``=`` does an exact match, multiple
|
|
102
|
+
whitespace-separated words become an ``AND`` of a substring match per
|
|
103
|
+
word, ``AND``/``OR`` combine sub-expressions left to right (no operator
|
|
104
|
+
precedence, same as the original), and anything else falls back to a
|
|
105
|
+
substring match.
|
|
106
|
+
|
|
107
|
+
A value that doesn't fit the field's type (e.g. a non-numeric string
|
|
108
|
+
against an integer field, or an invalid date) raises a DRF
|
|
109
|
+
``ValidationError`` — a clean ``400`` naming the offending field —
|
|
110
|
+
instead of an unhandled ``500``. Django validates most field types
|
|
111
|
+
eagerly, right when the filter is applied, so this is caught there
|
|
112
|
+
rather than only when the queryset is evaluated.
|
|
113
|
+
|
|
114
|
+
License
|
|
115
|
+
-------
|
|
116
|
+
|
|
117
|
+
MIT — see ``LICENSE``.
|
|
118
|
+
|
|
119
|
+
Development
|
|
120
|
+
-----------
|
|
121
|
+
|
|
122
|
+
.. code-block:: bash
|
|
123
|
+
|
|
124
|
+
pip install -e '.[dev]'
|
|
125
|
+
pytest
|
|
126
|
+
|
|
127
|
+
.. |build-status-image| image:: https://github.com/derafu/djangorestframework-tabulator/actions/workflows/ci.yml/badge.svg
|
|
128
|
+
:target: https://github.com/derafu/djangorestframework-tabulator/actions/workflows/ci.yml
|
|
129
|
+
:alt: CI
|
|
130
|
+
|
|
131
|
+
.. |pypi-version| image:: https://img.shields.io/pypi/v/djangorestframework-tabulator.svg
|
|
132
|
+
:target: https://pypi.org/project/djangorestframework-tabulator/
|
|
133
|
+
:alt: Pypi version
|
|
134
|
+
|
|
135
|
+
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/djangorestframework-tabulator.svg
|
|
136
|
+
:target: https://pypi.org/project/djangorestframework-tabulator/
|
|
137
|
+
:alt: Python versions
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "djangorestframework-tabulator"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Remote pagination and filtering for Tabulator (tabulator.info) on Django REST Framework."
|
|
9
|
+
readme = "README.rst"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Esteban De La Fuente Rubio / Derafu" },
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.10"
|
|
15
|
+
keywords = ["django", "djangorestframework", "tabulator", "pagination", "filtering"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Framework :: Django",
|
|
18
|
+
"Framework :: Django :: 4",
|
|
19
|
+
"Framework :: Django :: 5",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"django>=4.2",
|
|
26
|
+
"djangorestframework>=3.15",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"ruff",
|
|
32
|
+
"mypy",
|
|
33
|
+
"django-stubs",
|
|
34
|
+
"djangorestframework-stubs",
|
|
35
|
+
"pytest",
|
|
36
|
+
"pytest-django",
|
|
37
|
+
"pytest-cov",
|
|
38
|
+
"build",
|
|
39
|
+
"twine",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["rest_framework_tabulator"]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
DJANGO_SETTINGS_MODULE = "tests.settings"
|
|
47
|
+
python_files = ["test_*.py"]
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
|
|
50
|
+
[tool.mypy]
|
|
51
|
+
python_version = "3.10"
|
|
52
|
+
strict = true
|
|
53
|
+
files = ["rest_framework_tabulator"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
|
|
57
|
+
include = ["**/*.py", "**/*.pyi"]
|
|
58
|
+
|
|
59
|
+
exclude = [
|
|
60
|
+
".git",
|
|
61
|
+
"__pycache__",
|
|
62
|
+
".venv",
|
|
63
|
+
"venv",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
line-length = 79
|
|
67
|
+
target-version = "py310"
|
|
68
|
+
respect-gitignore = true
|
|
69
|
+
fix = true
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint]
|
|
72
|
+
|
|
73
|
+
select = [
|
|
74
|
+
"D",
|
|
75
|
+
"F",
|
|
76
|
+
"E",
|
|
77
|
+
"W",
|
|
78
|
+
"I",
|
|
79
|
+
"UP",
|
|
80
|
+
"N",
|
|
81
|
+
"B",
|
|
82
|
+
"A",
|
|
83
|
+
"C90",
|
|
84
|
+
"ARG",
|
|
85
|
+
"DTZ",
|
|
86
|
+
"TRY",
|
|
87
|
+
"SLF",
|
|
88
|
+
"ERA",
|
|
89
|
+
"PL",
|
|
90
|
+
"RUF",
|
|
91
|
+
"DJ",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
ignore = [
|
|
95
|
+
"D100",
|
|
96
|
+
"D101",
|
|
97
|
+
"D102",
|
|
98
|
+
"D104",
|
|
99
|
+
"D105",
|
|
100
|
+
"D106",
|
|
101
|
+
"D203",
|
|
102
|
+
"D212",
|
|
103
|
+
"D401",
|
|
104
|
+
"TRY400",
|
|
105
|
+
"N818",
|
|
106
|
+
"TRY003",
|
|
107
|
+
"ARG001",
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
[tool.ruff.lint.isort]
|
|
111
|
+
known-first-party = ["rest_framework_tabulator"]
|
|
112
|
+
|
|
113
|
+
[tool.ruff.lint.mccabe]
|
|
114
|
+
max-complexity = 25
|
|
115
|
+
|
|
116
|
+
[tool.ruff.lint.per-file-ignores]
|
|
117
|
+
"tests/**" = ["D", "SLF", "PLR2004"]
|
|
118
|
+
|
|
119
|
+
[tool.ruff.format]
|
|
120
|
+
quote-style = "single"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright (C) 2026 Esteban De La Fuente Rubio / Derafu <https://www.derafu.dev>
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Tabulator (tabulator.info) adapter for Django REST Framework."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from .filters import TabulatorFilterBackend
|
|
9
|
+
from .pagination import TabulatorPagination
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'TabulatorFilterBackend',
|
|
13
|
+
'TabulatorPagination',
|
|
14
|
+
]
|