Integration with Django-filter

django-rest-framework-datatables will always use icontains or iregex queries on all columns, which may be costly. More fine-grained control on the generated database queries can be achieved with Django-filter.

Integration with Django-filter is provided through Datatables-specific DatatablesFilterSet and DatatablesFilterBackend classes. These may be found in the django_filters subpackage.

Django-Filter Quickstart

Using the new DatatablesFilterBackend simply requires changing the import path. Instead of importing from rest_framework_datatables import from the django_filters subpackage.

from django_filters import filters
from rest_framework_datatables.django_filters.backends import DatatablesFilterBackend
from rest_framework_datatables.django_filters.filterset import DatatablesFilterSet
from rest_framework_datatables.django_filters.filters import GlobalFilter

class GlobalCharFilter(GlobalFilter, filters.CharFilter):
    pass


class AlbumGlobalFilter(DatatablesFilterSet):
    """Filter name, artist and genre by name with icontains"""

    name = GlobalCharFilter(lookup_expr='icontains')
    genres = GlobalCharFilter(field_name='genres__name', lookup_expr='icontains')
    artist = GlobalCharFilter(field_name='artist__name', lookup_expr='icontains')
    year = GlobalCharFilter()

    class Meta:
        model = Album
        fields = '__all__'


class AlbumGlobalViewSet(viewsets.ModelViewSet):
    queryset = Album.objects.all()
    serializer_class = AlbumSerializer
    filter_backends = (DatatablesFilterBackend,)
    filterset_class = AlbumGlobalFilter

Differences and Limitations

The data-data attribute or the columns[<n>][data] query parameter for the column must contain the name of the filter attribute on the filterset.

Although the DjangoFilterBackend uses the same parser for the queries generated by DataTables, the data-name attribute (or columns[<n>][name] query parameter) is completely ignored by the django-filter backend.

You can specify the lookup in the lookup_expr kwarg on the Filter. The ordering is implemented by looking at the field_name and lookup_expr attributes of the filters.

(Because of that, the keep parameter will not have the same semantics. It will cause the renderer to return the column, but it won’t be respected for global search by the backend and filterset. That means that only columns defined in the datatable can be filtered.)

The Django-Rest-Framework browsable api will not support global filters and the DataTables javascript frontend can’t take advantage of the automatic widgets generated by Django-filter.

Query Optimization

The above example will act as a drop-in replacement for the standard behaviour of django-rest-framework-datatables, which uses icontains and iregex for local and global queries.

With large tables this might generate very inefficient queries especially for non-string datatatypes.

By simply not using the GlobalFilter mixin, you can switch off global search per column to gain efficiency.

class AlbumGlobalFilter(AlbumFilter):
    """Filter name, artist and genre by name with icontains"""

    name = GlobalCharFilter(lookup_expr='icontains')
    genres = GlobalCharFilter(field_name='genres__name', lookup_expr='icontains')
    artist = GlobalCharFilter(field_name='artist__name', lookup_expr='icontains')

    class Meta:
        model = Album
        fields = '__all__'

This will revert the year field to the NumberFilter automatically generated by Django-filter which will require an exact number match.

Also you can use the capability of Django-filter to automatically generate the FilterSet for you:

class AlbumFilterViewSet(viewsets.ModelViewSet):
    queryset = Album.objects.all()
    serializer_class = AlbumSerializer
    filter_backends = [DatatablesFilterBackend]
    filterset_fields = '__all__'

In this case there will be no support for regular expressions, icontains or global searches, as Django-filter will use automatic lookups (e.g exact for strings), and you’ll need to add appropriate widgets to the datatable, because genres will need a multiple selection. It’s possible to use a javascript library such as yadcf to ease that task.

See the example app for an example of multiple selection using yadcf.

Customizing (global) queries

The defined filters will be used to filter the column search queries. Global queries are implemented with the optional global_q method on the GlobalFilter mixin. This will generate icontains or iregex lookups by default.

If you want more fine-grained control over queries, you can simply define your own filters.

Only filters that provide a global_q method will support global search queries.

The global_q method (as for example in the GlobalFilter mixin), should return a Q-object for the global field query. All these Q-objects will be combined with | (OR) and the resulting Q-object will be used used to filter the queryset that was returned by the applying the column filters.

This logic is identical to the one implemented by plain django-rest-framework-datatables.

Further Reading

It’s highly recommended to read the documentation of Django-filter.