(Comments)
AngularJS is the hottest client-side technology out there. Its cool nifty features coupled with a plethora of pre-built libraries is the reason why. The same goes for Django as a backend and hence more than often we find websites with AngularJS backed with Django.
This tutorial is for such developers who went with the angular-django option and want to implement a datatables in the client side with server-side processing(including sorting, and filtering) with ajax.
In this tutorial we work on Django and Django rest Framework. For Angular and HTML code and explanation refer PART 1 of this tutorial
We create a pagination base class to handle the query string format of datatables:
# Django Code # project/someapp/pagination.py from rest_framework.pagination import LimitOffsetPagination class DataTablePagination(LimitOffsetPagination): limit_query_param = 'length' offset_query_param = 'start'
We create a models as usual. Continuing with the same example as in the previous tutorial:
# Django Code # project/someapp/models.py from __future__ import unicode_literals from django.db import models class Department: name = models.CharField(max_length=30) class Employee(models.Model): name = models.CharField(max_length=30) company = models.CharField(max_length=30, blank=True, null=True) dept = models.ForeignKey(Department)
Also we create a nested the model serializers to spice things up a bit:
# Django Code # project/someapp/serializers.py from rest_framework import serializers from .models import * class DeptartmentSerializer(serializers.ModelSerializer): class Meta: model = Department fields = '__all__' class EmployeeSerializer(serializers.ModelSerializer): dept = class Meta: model = Employee fields = '__all__' def create(self, validated_data): validated_data['dept'] = Account.objects.get(id=validated_data['dept']['id']) return super(ContactSerializer, self).create(validated_data)
You could aslo easily append the update function in the serializer too in a similar fashion.
This is where all the magic happens! We could include our logic for searching, sorting and pagination in the view itself, but that would be ugly. Also it would not be reusable.
So first we create a BaseView which can then be inherited or sent in as a Mixin for the views to be consumed.
We create this BaseView First as follows. Note that here we are not using the filters module in django rest framework, which you could implement if you like:
# Django Code # project/someapp/views.py from rest_framework import generics from django.db.models import Q from .pagination import DataTablePagination class DataTableListCreateApi(generics.ListCreateAPIView): """ Base Class for DataTable Rest api server Provides support for datatable searching and sorting at server-side pass field names in search_parameters of all fields to be searched for the entered search query in datatable """ pagination_class = DataTablePagination search_parameters = () default_order_by = '' unfiltered_query_set = None def get_queryset(self): self.unfiltered_query_set = query_set = super(DataTableListCreateApi, self).get_queryset() order_by_index = int(self.request.query_params.get('order[0][column]', 0)) orderable = bool(self.request.query_params.get('columns[{}][orderable]'.format(order_by_index), 'false')) if order_by_index == 0 or not orderable: order_by_index = 1 order_by = self.request.query_params.get('columns[{}][data]'.format(order_by_index), self.default_order_by).replace('.', '__') order_by_dir = self.request.query_params.get('order[0][dir]', 'asc') if order_by_dir == 'desc': order_by = '-{}'.format(order_by) search_queries = self.request.query_params.get('search[value]', '').strip().split(' ') q = Q() if len(search_queries) > 0 and search_queries[0] != u'': for params in self.search_parameters: for query in search_queries: temp = { '{}__contains'.format(params): query, } q |= Q(**temp) query_set = query_set.filter(q) if order_by == '': return query_set return query_set.order_by(order_by) def get(self, request, *args, **kwargs): result = super(DataTableListCreateApi, self).get(request, *args, **kwargs) result.data['draw'] = int(request.query_params.get('draw', 0)) result.data['recordsFiltered'] = result.data['count'] result.data['recordsTotal'] = self.unfiltered_query_set.count() del result.data['count'] result.data['data']= result.data['results'] del result.data['results'] return result
This does not include the code for column-wise search functionality. We will keep that for a future tutorial.
Now creating the view for any api endpoint is very easy. Just append this to the above views.py.
# Django Code # project/someapp/views.py from .serializers import EmployeeSerializer class EmployeeView(DataTableListCreateApi): serializer_class = EmployeeSerializer search_parameters = ('name', 'dept__name') # state searchable fields default_order_by = 'name' queryset = Employee.objects.all()
That's it. Stay tuned for further updates and columnwise search functionality. Now you can experiment and tweak this method to suit your need! Don't forget to share your tweaks though, in the comments.
We develop web applications to our customers using python/django/angular.
Contact us at hello@cowhite.com
Comments