About
=====
Fork from django-datatables-view
Compatible with Django 2+ 3+ 4+

dj-datatables-view is a base view for handling server side
processing for the awesome datatables 1.9.*, 1.10.*, 1.11.*
http://datatables.net

dj-datatables-view simplifies handling of sorting, filtering and
creating JSON output, as defined at:
https://datatables.net/examples/server_side

Usage
=====

1. Install dj-datatables-view
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

::

    pip install dj-datatables-view

2. Edit views.py
~~~~~~~~~~~~~~~~

*django\_datatables\_view* uses **GenericViews**, so your view should
just inherit from base class: **BaseDatatableView**, and override few
things (there is also a DatatableMixin - pure datatables handler that
can be used with the mixins of your choice, eg. django-braces). These
are:

-  **model** - the model that should be used to populate the datatable
-  **columns** - the columns that are going to be displayed. If not
   defined then django\_datatables\_view will look for 'name' in the
   columns definition provided in the request by DataTables, eg.:
   columnDefs: [{name: 'name', targets: [0]} (only works for datatables
   1.10+)
-  **order\_columns** - list of column names used for sorting (eg. if
   user sorts by second column then second column name from this list
   will be used with order by clause). If not defined then
   django\_datatables\_view will look for 'name' in the columns
   definition provided in the request by DataTables, eg.: columnDefs:
   [{name: 'name', targets: [0]} (only works for datatables 1.10+)
-  **filter\_queryset** - if you want to filter your DataTable in some
   specific way then override this method. In case of older DataTables
   (pre 1.10) you need to override this method or there will be no
   filtering.
-  **filter\_method** - returns 'istartswith' by default, you can
   override it to use different filtering method, e.g. icontains: return
   self.FILTER\_ICONTAINS

For more advanced customisation you might want to override:

-  **get\_initial\_queryset** - method that should return queryset used
   to populate datatable
-  **prepare\_results** - this method should return list of lists (rows
   with columns) as needed by datatables
-  **escape\_values** - you can set this attribute to False in order to
   not escape values returned from render\_column method

The code is rather simple so do not hesitate to have a look at it.
Method that is executed first (and that calls other methods to execute
whole logic) is **get\_context\_data**. Definitely have a look at this
method!

See example below:

.. code:: python


        from dj-datatables-view.base_datatable_view import BaseDatatableView
        from django.utils.html import escape

        class OrderListJson(BaseDatatableView):
            # The model we're going to show
            model = MyModel

            # define the columns that will be returned
            columns = ['number', 'user', 'state', 'created', 'modified']

            # define column names that will be used in sorting
            # order is important and should be same as order of columns
            # displayed by datatables. For non sortable columns use empty
            # value like ''
            order_columns = ['number', 'user', 'state', '', '']

            # set max limit of records returned, this is used to protect our site if someone tries to attack our site
            # and make it return huge amount of data
            max_display_length = 500

            def render_column(self, row, column):
                # We want to render user as a custom column
                if column == 'user':
                    # escape HTML for security reasons
                    return escape('{0} {1}'.format(row.customer_firstname, row.customer_lastname))
                else:
                    return super(OrderListJson, self).render_column(row, column)

            def filter_queryset(self, qs):
                # use parameters passed in GET request to filter queryset

                # simple example:
                search = self.request.GET.get('search[value]', None)
                if search:
                    qs = qs.filter(name__istartswith=search)

                # more advanced example using extra parameters
                filter_customer = self.request.GET.get('customer', None)

                if filter_customer:
                    customer_parts = filter_customer.split(' ')
                    qs_params = None
                    for part in customer_parts:
                        q = Q(customer_firstname__istartswith=part)|Q(customer_lastname__istartswith=part)
                        qs_params = qs_params | q if qs_params else q
                    qs = qs.filter(qs_params)
                return qs

3. Edit urls.py
~~~~~~~~~~~~~~~

Add typical django's urlconf entry:

.. code:: python

    url(r'^my/datatable/data/$', login_required(OrderListJson.as_view()), name='order_list_json'),

4. Define HTML + JavaScript
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Example JS:

.. code:: javascript

    $(document).ready(function() {
        var oTable = $('.datatable').dataTable({
            // ...
            "processing": true,
            "serverSide": true,
            "ajax": "{% url 'order_list_json' %}"
        });
        // ...
    });

Another example of views.py customisation
-----------------------------------------

.. code:: python

    from dj-datatables-view.base_datatable_view import BaseDatatableView
    from django.utils.html import escape

    class OrderListJson(BaseDatatableView):
        order_columns = ['number', 'user', 'state']

        def get_initial_queryset(self):
            # return queryset used as base for futher sorting/filtering
            # these are simply objects displayed in datatable
            # You should not filter data returned here by any filter values entered by user. This is because
            # we need some base queryset to count total number of records.
            return MyModel.objects.filter(something=self.kwargs['something'])

        def filter_queryset(self, qs):
            # use request parameters to filter queryset

            # simple example:
            search = self.request.GET.get('search[value]', None)
            if search:
                qs = qs.filter(name__istartswith=search)

            # more advanced example
            filter_customer = self.request.GET.get('customer', None)

            if filter_customer:
                customer_parts = filter_customer.split(' ')
                qs_params = None
                for part in customer_parts:
                    q = Q(customer_firstname__istartswith=part)|Q(customer_lastname__istartswith=part)
                    qs_params = qs_params | q if qs_params else q
                qs = qs.filter(qs_params)
            return qs

        def prepare_results(self, qs):
            # prepare list with output column data
            # queryset is already paginated here
            json_data = []
            for item in qs:
                json_data.append([
                    escape(item.number),  # escape HTML for security reasons
                    escape("{0} {1}".format(item.customer_firstname, item.customer_lastname)),  # escape HTML for security reasons
                    item.get_state_display(),
                    item.created.strftime("%Y-%m-%d %H:%M:%S"),
                    item.modified.strftime("%Y-%m-%d %H:%M:%S")
                ])
            return json_data

Yet another example of views.py customisation
---------------------------------------------

This sample assumes that list of columns and order columns is defined on
the client side (DataTables), eg.:

.. code:: javascript

    $(document).ready(function() {
        var dt_table = $('.datatable').dataTable({
            order: [[ 0, "desc" ]],
            columnDefs: [
                {
                    name: 'name',
                    orderable: true,
                    searchable: true,
                    targets: [0]
                },
                {
                    name: 'description',
                    orderable: true,
                    searchable: true,
                    targets: [1]
                }
            ],
            searching: true,
            processing: true,
            serverSide: true,
            stateSave: true,
            ajax: TESTMODEL_LIST_JSON_URL
        });
    });

.. code:: python

    class TestModelListJson(BaseDatatableView):
        model = TestModel
