diff --git a/ajax_datatable/static/ajax_datatable/js/utils.js b/ajax_datatable/static/ajax_datatable/js/utils.js index cb997f3..8229e81 100644 --- a/ajax_datatable/static/ajax_datatable/js/utils.js +++ b/ajax_datatable/static/ajax_datatable/js/utils.js @@ -528,13 +528,55 @@ window.AjaxDatatableViewUtils = (function() { table.DataTable().ajax.reload(null, false); } + // Export table using last draw params. + function export_table(element, url, extra_data={}) { + let data = {action: 'export'}; + if (extra_data) { + Object.assign(data, extra_data); + } + + let table = element.DataTable(); + + Object.assign(data, table.ajax.params()); + + $.ajax({ + type: 'POST', + url: url, + data: data, + xhrFields: { + responseType: 'blob' + }, + cache: false, + crossDomain: false, + headers: {'X-CSRFToken': getCSRFToken()}, + success: function(result, status, xhr){ + const filename = xhr.getResponseHeader('content-disposition').split('filename=')[1].split(';')[0]; + const url = window.URL.createObjectURL(result); + const a = document.createElement('a'); + + a.style.display = 'none'; + a.href = url; + a.download = filename + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + }, + error: function(result){ + console.log(result) + } + }).done(function(data, textStatus, jqXHR){ + + }) + } + return { init: init, initialize_table: initialize_table, adjust_table_columns: adjust_table_columns, redraw_all_tables: redraw_all_tables, - redraw_table: redraw_table + redraw_table: redraw_table, + export_table: export_table }; })(); diff --git a/ajax_datatable/views.py b/ajax_datatable/views.py index d259473..12f9695 100644 --- a/ajax_datatable/views.py +++ b/ajax_datatable/views.py @@ -4,6 +4,8 @@ import datetime import json +import io +import csv from django.views.generic import View from django.http.response import HttpResponse, HttpResponseBadRequest from django.core.exceptions import FieldDoesNotExist @@ -407,6 +409,19 @@ def dispatch(self, request, *args, **kwargs): 'html': self.render_row_details(row_id, request), 'parent-row-id': row_id, }) + elif action == 'export': + rows = self.export_table(request, *args, **kwargs) + + buffer = io.StringIO() # python 2 needs io.BytesIO() instead + wr = csv.writer(buffer, quoting=csv.QUOTE_ALL) + wr.writerow(rows[0].keys()) + for row in rows: + wr.writerow(row.values()) + + buffer.seek(0) + response = HttpResponse(buffer, content_type='text/csv') + response['Content-Disposition'] = f'attachment; filename={self.title.lower()}_datatable_export.csv' + return response response = super(AjaxDatatableView, self).dispatch(request, *args, **kwargs) else: @@ -1001,3 +1016,50 @@ def list_autofilter_choices(self, request, column_spec, field, initial_search_va print('ERROR: ' + str(e)) choices = [] return choices + + def export_table(self, request, *args, **kwargs): + try: + query_dict = request.REQUEST + params = self.read_parameters(query_dict) + except ValueError: + return HttpResponseBadRequest() + + if TRACE_QUERYDICT: + trace(json.dumps(query_dict, indent=2, cls=DjangoJSONEncoder), prompt='query_dict') + trace(params, prompt='params', prettify=True) + + # Prepare the queryset and apply the search and order filters + qs = self.get_initial_queryset(request) + if not DISABLE_QUERYSET_OPTIMIZATION and not self.disable_queryset_optimization: + if ( + self.disable_queryset_optimization_select_related and + self.disable_queryset_optimization_only and self.disable_queryset_optimization_prefetch_related + ): + pass + else: + qs = self.optimize_queryset(qs) + qs = self.prepare_queryset(params, qs) + if TRACE_QUERYSET: + prettyprint_queryset(qs) + + # Slice result + # paginator = Paginator(qs, params['length'] if params['length'] != -1 else qs.count()) + + columns = [c['name'] for c in self.column_specs] + json_data = [] + for cur_object in qs: + retdict = { + # fieldname: '