Skip to main content

Django views with javascript filters using Datatables

Project description

PyPI version

django-filtered-datatables

A Django library for building interactive data tables powered by DataTables.js, with server-side processing, dynamic JavaScript filters, inline editing, and a rich plugin system.

Define your tables in Python using Django models and ORM queries -- columns, filters, sorting, annotations, and rendering are all configured from your views, with minimal JavaScript required.

Features

  • 20+ column types -- dates, currency, booleans, links, many-to-many, choice fields, lambdas, and more
  • AJAX and non-AJAX modes -- load data dynamically or render inline
  • JavaScript filters -- pivot, tag, select2, date range, and text search filters
  • Modal filter dialogs -- form-based filtering with crispy-forms support
  • Inline editing -- edit cells directly in the table with select2 dropdowns or text inputs
  • Row selection -- checkbox-based row selection with bulk actions
  • Plugins -- column totals, conditional row colouring, drag-and-drop reordering, save/restore filter state
  • Data export -- Excel (.xlsx) download and clipboard copy
  • Client-side rendering -- render functions for badges, links, conditional formatting, and lookups
  • Django ORM integration -- annotations, aggregations, foreign key traversal, callable model methods
  • Model-defined columns -- configure columns directly on your Django models via an inner Datatable class
  • Multiple tables per view -- display several tables on a single page
  • Spreadsheet mode -- JSpreadsheet integration for spreadsheet-like editing
  • Form widgets -- DataTableWidget and DataTableReorderWidget for use in Django forms
  • Mobile responsive -- automatic device detection with per-device column visibility
  • Redis caching -- optional cache layer with signal-based invalidation

Installation

pip install django-filtered-datatables

Add to your INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    'django_datatables',
    'ajax_helpers',
    # ...
]

Include the required JavaScript and CSS in your base template. The library depends on DataTables.js, jQuery, and Bootstrap.

Quick Start

1. Define a view

from django_datatables.datatables import DatatableView

class CompanyList(DatatableView):
    model = Company

    @staticmethod
    def setup_table(table):
        table.add_columns(
            'id',
            'name',
            'dissolved',
        )

2. Wire up the URL

urlpatterns = [
    path('companies/', CompanyList.as_view(), name='company-list'),
]

3. Use the template

The default template renders a complete DataTables table. You can override it via template_name on your view.

Usage

Adding Columns

Columns can be added in several ways:

def setup_table(table):
    table.add_columns(
        'name',                                          # model field name
        ('id', {'title': 'ID', 'width': '30px'}),       # tuple with options
        DateColumn('date_entered', column_name='Date'),  # column class instance
        'company__name',                                 # foreign key traversal
        'Tags',                                          # model-defined column
    )

String prefix syntax for quick column options:

  • .field -- hidden column (still available for filtering/rendering)
  • _field -- calculated column (not from the database)
  • $field -- secure column (hidden from client source)

Column Types

Column Purpose
DatatableColumn Standard column with customisable row_result and col_setup
DateColumn Formats dates (dd/mm/yyyy)
DateTimeColumn Formats datetimes
ChoiceColumn Maps integer choices to display labels
CurrencyColumn Currency formatting
BooleanColumn Boolean display
ColumnLink Clickable link using Django URL names
ManyToManyColumn Displays many-to-many relationships
CallableColumn Calls a model method
LambdaColumn Processes values with a lambda function
SelectColumn Row selection checkboxes
TextFieldColumn Long text with truncation

Model-Defined Columns

Configure columns directly on your models:

class Company(models.Model):
    name = models.CharField(max_length=80)

    class Datatable(DatatableModel):
        # Annotation-based column
        people = {'annotations': {'people': Count('person__id')}}

        # Link column
        collink_1 = ColumnLink(title='View', field=['id', 'name'], url_name='company-detail')

        # Column list (renders multiple fields as a group)
        company_list = ['id', 'name']

        # Custom column class
        class ModelIdColumn(DatatableColumn):
            def col_setup(self):
                self.field = 'id'
                self.title = 'Custom ID'

These columns can then be referenced by name in add_columns():

table.add_columns('id', 'name', 'people', 'collink_1', 'ModelIdColumn')

Filters

def setup_table(table):
    table.add_columns('id', 'name', 'company__name', 'Tags')

    # JavaScript filters
    table.add_js_filters('tag', 'Tags')                    # tag filter
    table.add_js_filters('select2', 'company__name')       # select2 dropdown
    table.add_js_filters('totals', 'id')                   # totals/count filter
    table.add_js_filters('date', 'date_entered')           # date range
    table.add_js_filters('expand', 'level', id_column='id')  # tree expand

    # ORM-level filters
    table.filter = {'company__id': 1}
    table.exclude = {'dissolved': True}

Modal Filter (form-based)

from django_datatables.modal_filter.mixins import DatatableFilterMixin, DatatableFilterField

class CompanyList(DatatableFilterMixin, DatatableView):
    model = Company
    filter_fields = [
        DatatableFilterField('Company Name', CharField(required=False), datatable_field='name__contains'),
        DatatableFilterField('Tags', FilterModelMultipleChoiceField(queryset=Tags.objects.all()), datatable_field='tags__in'),
    ]

Sorting

table.sort('name')           # ascending
table.sort('-name')          # descending
table.sort('name', '-id')   # multi-column

Column Search

Per-column search boxes appear below the header by default. Customise them:

table.add_columns(
    ColumnBase(column_name='status', field='dissolved',
               col_search_select=[['true', 'Dissolved'], ['false', 'Active']]),
    ColumnBase(column_name='order', field='order', no_col_search=True),
)
table.table_options['no_col_search'] = True  # disable all column search

Client-Side Rendering

Render functions transform cell data in the browser without extra server round-trips:

from django_datatables.helpers import render_replace, row_button

table.add_columns(
    # Badge rendering
    ColumnBase(column_name='people', field='people', render=[
        render_replace(column='people', html='<span class="badge badge-primary">%1%</span>'),
    ]),

    # Row action button
    ColumnBase(column_name='action', render=[row_button('delete', 'Delete')]),

    # Lookup-based rendering
    ('id', {'title': 'Status', 'render': [
        {'function': 'ReplaceLookup', 'html': '%1%', 'var': '%1%'}
    ], 'lookup': [[1, 'Active'], [2, 'Inactive']]}),
)

Available render functions: Replace, ReplaceLookup, Html, MergeArray, ValueInColumn.

Plugins

from django_datatables.plugins.colour_rows import ColourRows
from django_datatables.plugins.column_totals import ColumnTotals
from django_datatables.plugins.reorder import Reorder

# Conditional row colouring
table.add_plugin(ColourRows, [
    {'column': 'status', 'values': {'overdue': 'table-danger', 'pending': 'table-warning'}}
])

# Column totals in footer
table.add_plugin(ColumnTotals, {
    'amount': {'sum': True},
    'id': {'text': 'Total', 'css_class': 'text-danger'},
    'percentage': {'sum': 'percentage', 'numerator': 'vans', 'denominator': 'total', 'decimal_places': 1},
})

# Drag-and-drop row reordering
table.add_plugin(Reorder)

Inline Editing

def setup_table(table):
    table.edit_fields = ['first_name', 'title']
    table.edit_options = {'company__name': {'select2': True}}

Row Actions (AJAX Commands)

Handle row-level actions from buttons:

class MyView(DatatableView):
    ajax_commands = ['row']

    def row_delete(self, **kwargs):
        return self.command_response('delete_row', row_no=kwargs['row_no'], table_id=kwargs['table_id'])

    def row_toggle_tag(self, **kwargs):
        row_data = json.loads(kwargs['row_data'])
        # ... process action ...
        table = self.tables[kwargs['table_id']]
        return table.refresh_row(self.request, kwargs['row_no'])

Data Export

from django_datatables.downloads.excel_download import ExcelDownload
from django_datatables.downloads.clipboard import ClipboardCopy

class MyView(ExcelDownload, ClipboardCopy, DatatableView):
    model = MyModel

    def setup_menu(self):
        self.add_menu('menu', 'buttons').add_items(
            self.download_menu_item(),
            self.clipboard_menu_item(),
        )
        super().setup_menu()

Multiple Tables Per View

class TwoTableView(DatatableView):
    template_name = 'two_tables.html'

    def add_tables(self):
        self.add_table('t1', model=Company)
        self.add_table('t2', model=Person)

    @staticmethod
    def setup_t1(table):
        table.add_columns('id', 'name')

    @staticmethod
    def setup_t2(table):
        table.add_columns('id', 'first_name')

Non-Model Data

Load data from any source (JSON files, APIs, etc.):

class JsonTableView(DatatableView):

    @staticmethod
    def setup_table(table):
        table.add_columns(
            ColumnBase(column_name='field1'),
            ColumnBase(column_name='field2'),
        )

    @staticmethod
    def get_table_query(table, **kwargs):
        with open('data.json') as f:
            return json.load(f)

Custom Column Classes

from django_datatables.columns import DatatableColumn

class FullName(DatatableColumn):
    def col_setup(self):
        self.field = ['first_name', 'surname']

    def row_result(self, data_dict, _page_results):
        return f'{data_dict["first_name"]} {data_dict["surname"]}'

Table Options

Common options passed via table.table_options:

table.table_options['pageLength'] = 50
table.table_options['no_col_search'] = True
table.table_options['scrollX'] = True
table.table_options['ajax_url'] = '/custom/ajax/url/'
table.table_options['row_href'] = [render_replace(column='id', html='/detail/%1%/')]

Demo Application

A Docker-based demo is included with sample data and 15+ examples covering filters, plugins, editing, rendering, and more.

docker-compose up

Then visit http://localhost:8006.

Dependencies

Optional:

  • openpyxl -- for Excel export
  • redis -- for table caching

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_filtered_datatables-0.1.0.tar.gz (332.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_filtered_datatables-0.1.0-py3-none-any.whl (369.0 kB view details)

Uploaded Python 3

File details

Details for the file django_filtered_datatables-0.1.0.tar.gz.

File metadata

File hashes

Hashes for django_filtered_datatables-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1887f4d2baa6695929e50df22443efbeb4f917a3fe429b3e42cefe8ab40a6605
MD5 71d61d9f2e4ed579fa272d0f254b2778
BLAKE2b-256 de341d0095b85788c0c950925c2071cb0e4574f1dae48301db56b82cbd59fcfa

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_filtered_datatables-0.1.0.tar.gz:

Publisher: publish.yml on jonesim/django-datatables

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_filtered_datatables-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_filtered_datatables-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 442ab447d8c82241d1f4f115c060e81626eb07b9ed0b4c29f0e3fa7bd5d3cf89
MD5 c48a1054c8d14249838a909e09769eb4
BLAKE2b-256 a65a7cbde65ccc398734f1d94a47dd56cdd8fb36dd015cc57656eb5b52e052b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_filtered_datatables-0.1.0-py3-none-any.whl:

Publisher: publish.yml on jonesim/django-datatables

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page