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.