Angular-Datatables with Django rest-framework (Part 2) - Django

(Comments)



Who is this tutorial for?

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.



Angular and HTML

In this tutorial we work on Django and Django rest Framework. For Angular and HTML code and explanation refer PART 1 of this tutorial



Pagination

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'



Model and Serializer

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.



View

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.

Comments

Recent Posts

Archive

2021
2020
2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom