Metadata-Version: 2.5
Name: djangorestframework-tabulator
Version: 0.1.0
Summary: Remote pagination and filtering for Tabulator (tabulator.info) on Django REST Framework.
Author: Esteban De La Fuente Rubio / Derafu
License: MIT License
        
        Copyright (C) 2026 Esteban De La Fuente Rubio / Derafu <https://www.derafu.dev>
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: django,djangorestframework,filtering,pagination,tabulator
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4
Classifier: Framework :: Django :: 5
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.10
Requires-Dist: django>=4.2
Requires-Dist: djangorestframework>=3.15
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: django-stubs; extra == 'dev'
Requires-Dist: djangorestframework-stubs; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest-django; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/x-rst

djangorestframework-tabulator
==============================

|build-status-image| |pypi-version| |py-versions|

Remote pagination and filtering for `Tabulator <https://tabulator.info>`_
on top of `Django REST Framework <https://www.django-rest-framework.org>`_.

Why this exists
----------------

Tabulator, in remote mode (``pagination: "remote"``, ``filterMode:
"remote"``, ``sortMode: "remote"``), sends pagination/filtering/sorting
using its own query string convention, and expects the response back in
its own shape — neither matches what DRF ships out of the box
(``PageNumberPagination`` responds with ``{count, next, previous,
results}``; its ``filter_backends`` don't understand nested bracketed
keys). This package translates both sides. `jQuery DataTables
<https://datatables.net>`_ already has a mature DRF adapter
(`djangorestframework-datatables
<https://pypi.org/project/djangorestframework-datatables/>`_); no
equivalent exists for Tabulator.

Usage
-----

.. code-block:: python

    from rest_framework.generics import ListAPIView
    from rest_framework_tabulator import TabulatorFilterBackend, TabulatorPagination

    class DocumentListView(ListAPIView):
        queryset = Document.objects.all()
        serializer_class = DocumentSerializer
        pagination_class = TabulatorPagination
        filter_backends = [TabulatorFilterBackend]
        filterset_fields = ['number', 'status', 'issued_at']
        ordering_fields = ['issued_at', 'number', 'total']

Tabulator's contract (confirmed against the source code of
``tabulator-tables/tabulator`` itself, not third-party documentation):

- Request: ``page``/``size`` (pagination), ``sort[i][field]``/
  ``sort[i][dir]`` (sorting), ``filter[i][field]``/``filter[i][type]``/
  ``filter[i][value]`` (filtering) — an array value (the ``in`` type) is
  sent as ``filter[i][value][0]``, ``filter[i][value][1]``, etc.
- Expected response: ``{"data": [...], "last_page": N}``.

``filterset_fields``/``ordering_fields`` on the view are allowlists of
fields (same attribute names ``django-filter``/``rest_framework.filters
.OrderingFilter`` already use, so they look familiar) — any field not
declared there is silently ignored, on both the filter and the sort
side. Leaving either one undeclared denies filtering/sorting entirely
rather than allowing every field by default.

Supported filter types
-----------------------

Every filter type Tabulator ships is supported (confirmed against
``Filter/defaults/filters.js`` in the Tabulator source for the exact
set of built-in types):

.. list-table::
   :header-rows: 1

   * - Tabulator ``type``
     - Django lookup
   * - ``=``
     - exact
   * - ``!=``
     - negated exact
   * - ``<``, ``<=``, ``>``, ``>=``
     - ``lt``, ``lte``, ``gt``, ``gte``
   * - ``like``
     - ``icontains``
   * - ``keywords``
     - ``icontains`` (see note below)
   * - ``starts``
     - ``istartswith``
   * - ``ends``
     - ``iendswith``
   * - ``regex``
     - ``regex``
   * - ``in``
     - ``in`` (array value)
   * - ``smart``
     - ported from Tabulator's own ``smart`` filter
   * - ``smarter``
     - ported from Tabulator's own ``smarter`` filter

``keywords`` note: Tabulator's client-side ``keywords`` filter also
supports a custom word separator and an "all words must match" toggle
(``headerFilterFuncParams``), but neither is sent to the server in
remote mode — the ajax payload only ever carries ``{field, type,
value}``. It is approximated here as a plain substring match, same as
``like``.

``smart``/``smarter`` are a faithful port of Tabulator's own filter
functions, not a Django-specific addition: ``.`` matches any non-empty
value, ``!`` matches empty/null, a leading comparison operator does a
numeric comparison, a leading ``=`` does an exact match, multiple
whitespace-separated words become an ``AND`` of a substring match per
word, ``AND``/``OR`` combine sub-expressions left to right (no operator
precedence, same as the original), and anything else falls back to a
substring match.

A value that doesn't fit the field's type (e.g. a non-numeric string
against an integer field, or an invalid date) raises a DRF
``ValidationError`` — a clean ``400`` naming the offending field —
instead of an unhandled ``500``. Django validates most field types
eagerly, right when the filter is applied, so this is caught there
rather than only when the queryset is evaluated.

License
-------

MIT — see ``LICENSE``.

Development
-----------

.. code-block:: bash

    pip install -e '.[dev]'
    pytest

.. |build-status-image| image:: https://github.com/derafu/djangorestframework-tabulator/actions/workflows/ci.yml/badge.svg
   :target: https://github.com/derafu/djangorestframework-tabulator/actions/workflows/ci.yml
   :alt: CI

.. |pypi-version| image:: https://img.shields.io/pypi/v/djangorestframework-tabulator.svg
   :target: https://pypi.org/project/djangorestframework-tabulator/
   :alt: Pypi version

.. |py-versions| image:: https://img.shields.io/pypi/pyversions/djangorestframework-tabulator.svg
   :target: https://pypi.org/project/djangorestframework-tabulator/
   :alt: Python versions
