From df1fd16a794669f2dfc1b5dbd85add427ab3a44a Mon Sep 17 00:00:00 2001 From: Benjamin Bertrand <benjamin.bertrand@esss.se> Date: Wed, 3 Jan 2018 14:24:06 +0100 Subject: [PATCH] Switch to server-side processing for items table When there are more than a 1000 items in the table, retrieving and displaying all items takes about 7 seconds. This is every time we refresh the items page, which is too slow. --- app/inventory/views.py | 95 +++++++++++++++++++++++++----- app/static/js/items.js | 19 +++--- app/templates/inventory/items.html | 1 - 3 files changed, 88 insertions(+), 27 deletions(-) diff --git a/app/inventory/views.py b/app/inventory/views.py index 3978a6d..bcd6c00 100644 --- a/app/inventory/views.py +++ b/app/inventory/views.py @@ -24,20 +24,87 @@ bp = Blueprint('inventory', __name__) @bp.route('/_retrieve_items') @login_required def retrieve_items(): - items = models.Item.query.order_by(models.Item.created_at) - data = [[item.id, - item.ics_id, - utils.format_field(item.created_at), - utils.format_field(item.updated_at), - item.serial_number, - utils.format_field(item.manufacturer), - utils.format_field(item.model), - utils.format_field(item.location), - utils.format_field(item.status), - utils.format_field(item.parent), - '\n'.join([comment.body for comment in item.comments]), - ] for item in items] - return jsonify(data=data) + # Get the parameters from the query string sent by datatables + draw = int(request.args.get('draw')) + start = int(request.args.get('start', 0)) + per_page = int(request.args.get('length', 20)) + search = request.args.get('search[value]', '') + order_column = int(request.args.get('order[0][column]')) + if request.args.get('order[0][dir]') == 'desc': + order_dir = sa.desc + else: + order_dir = sa.asc + # Total number of items before filtering + nb_items_total = db.session.query(sa.func.count(models.Item.id)).scalar() + # Construct the query + query = models.Item.query + if search: + if '%' not in search: + search = f'%{search}%' + q1 = query.filter( + sa.or_( + models.Item.ics_id.like(search), + models.Item.serial_number.like(search), + ) + ) + q2 = query.join(models.Item.manufacturer).filter( + models.Manufacturer.name.like(search)) + q3 = query.join(models.Item.model).filter( + models.Model.name.like(search)) + q4 = query.join(models.Item.location).filter( + models.Location.name.like(search)) + q5 = query.join(models.Item.status).filter( + models.Status.name.like(search)) + q6 = query.join(models.Item.comments).filter( + models.ItemComment.body.like(search)) + query = q1.union(q2).union(q3).union(q4).union(q5).union(q6) + nb_items_filtered = query.order_by(None).count() + else: + nb_items_filtered = nb_items_total + # Construct the order_by query + columns = ('id', + 'ics_id', + 'created_at', + 'updated_at', + 'serial_number', + 'manufacturer', + 'model', + 'location', + 'status', + 'parent', + ) + field = getattr(models.Item, columns[order_column]) + if 4 < order_column < 9: + query = query.join(field) + relationship_class = getattr(models, columns[order_column].title()) + relationship_field = getattr(relationship_class, 'name') + query = query.order_by(order_dir(relationship_field)) + else: + query = query.order_by(order_dir(getattr(models.Item, columns[order_column]))) + # Limit and offset the query + if per_page != -1: + query = query.limit(per_page) + query = query.offset(start) + data = [ + [item.id, + item.ics_id, + utils.format_field(item.created_at), + utils.format_field(item.updated_at), + item.serial_number, + utils.format_field(item.manufacturer), + utils.format_field(item.model), + utils.format_field(item.location), + utils.format_field(item.status), + utils.format_field(item.parent), + ] for item in query.all() + ] + response = { + 'draw': draw, + 'recordsTotal': nb_items_total, + 'recordsFiltered': nb_items_filtered, + 'data': data + } + return jsonify(response) @bp.route('/items') diff --git a/app/static/js/items.js b/app/static/js/items.js index d9ecbd2..75dbff6 100644 --- a/app/static/js/items.js +++ b/app/static/js/items.js @@ -47,14 +47,14 @@ $(document).ready(function() { }); var items_table = $("#items_table").DataTable({ - "ajax": function(data, callback, settings) { - $.getJSON( - $SCRIPT_ROOT + "/inventory/_retrieve_items", - function(json) { - callback(json); - }); + "ajax": { + "url": $SCRIPT_ROOT + "/inventory/_retrieve_items" }, + "processing": true, + "serverSide": true, + "searchDelay": 500, "order": [[3, 'desc']], + "orderMulti": false, "pagingType": "full_numbers", "pageLength": 20, "lengthMenu": [[20, 50, 100, -1], [20, 50, 100, "All"]], @@ -74,12 +74,7 @@ $(document).ready(function() { var url = $SCRIPT_ROOT + "/inventory/items/view/" + data; return '<a href="'+ url + '">' + data + '</a>'; } - }, - { - "targets": [10], - "visible": false, - "searchable": true - }, + } ] }); diff --git a/app/templates/inventory/items.html b/app/templates/inventory/items.html index c566b50..9e4edba 100644 --- a/app/templates/inventory/items.html +++ b/app/templates/inventory/items.html @@ -31,7 +31,6 @@ <th>Location</th> <th>Status</th> <th>Parent</th> - <th>Comments</th> </tr> </thead> </table> -- GitLab