Django views with javascript filters using Datatables
Project description
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
Datatableclass - Multiple tables per view -- display several tables on a single page
- Spreadsheet mode -- JSpreadsheet integration for spreadsheet-like editing
- Form widgets --
DataTableWidgetandDataTableReorderWidgetfor 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
- Python >= 3.6
- Django
- django-ajax-helpers >= 0.0.16
Optional:
openpyxl-- for Excel exportredis-- for table caching
License
MIT
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_filtered_datatables-0.1.0.tar.gz.
File metadata
- Download URL: django_filtered_datatables-0.1.0.tar.gz
- Upload date:
- Size: 332.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 |
1887f4d2baa6695929e50df22443efbeb4f917a3fe429b3e42cefe8ab40a6605
|
|
| MD5 |
71d61d9f2e4ed579fa272d0f254b2778
|
|
| BLAKE2b-256 |
de341d0095b85788c0c950925c2071cb0e4574f1dae48301db56b82cbd59fcfa
|
Provenance
The following attestation bundles were made for django_filtered_datatables-0.1.0.tar.gz:
Publisher:
publish.yml on jonesim/django-datatables
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_filtered_datatables-0.1.0.tar.gz -
Subject digest:
1887f4d2baa6695929e50df22443efbeb4f917a3fe429b3e42cefe8ab40a6605 - Sigstore transparency entry: 2172046454
- Sigstore integration time:
-
Permalink:
jonesim/django-datatables@26f18cefa937fec1a1712b470002c4d948f04381 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jonesim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@26f18cefa937fec1a1712b470002c4d948f04381 -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_filtered_datatables-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_filtered_datatables-0.1.0-py3-none-any.whl
- Upload date:
- Size: 369.0 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 |
442ab447d8c82241d1f4f115c060e81626eb07b9ed0b4c29f0e3fa7bd5d3cf89
|
|
| MD5 |
c48a1054c8d14249838a909e09769eb4
|
|
| BLAKE2b-256 |
a65a7cbde65ccc398734f1d94a47dd56cdd8fb36dd015cc57656eb5b52e052b7
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_filtered_datatables-0.1.0-py3-none-any.whl -
Subject digest:
442ab447d8c82241d1f4f115c060e81626eb07b9ed0b4c29f0e3fa7bd5d3cf89 - Sigstore transparency entry: 2172046460
- Sigstore integration time:
-
Permalink:
jonesim/django-datatables@26f18cefa937fec1a1712b470002c4d948f04381 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jonesim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@26f18cefa937fec1a1712b470002c4d948f04381 -
Trigger Event:
release
-
Statement type: