diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 64b08f06b5..f157851215 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -6,6 +6,9 @@ Provides a JSON API for the Part app from __future__ import unicode_literals from django_filters.rest_framework import DjangoFilterBackend +from django.conf import settings + +from django.db.models import Sum from rest_framework import status from rest_framework.response import Response @@ -15,6 +18,8 @@ from rest_framework import generics, permissions from django.conf.urls import url, include from django.urls import reverse +import os + from .models import Part, PartCategory, BomItem, PartStar from .serializers import PartSerializer, BomItemSerializer @@ -99,6 +104,61 @@ class PartList(generics.ListCreateAPIView): serializer_class = PartSerializer + def list(self, request, *args, **kwargs): + """ + Instead of using the DRF serialiser to LIST, + we serialize the objects manuually. + This turns out to be significantly faster. + """ + + queryset = self.filter_queryset(self.get_queryset()) + + data = queryset.values( + 'pk', + 'category', + 'image', + 'name', + 'IPN', + 'description', + 'keywords', + 'is_template', + 'URL', + 'units', + 'trackable', + 'assembly', + 'component', + 'salable', + 'active', + ).annotate( + in_stock=Sum('stock_items__quantity'), + ) + + # TODO - Annotate total being built + # TODO - Annotate total on order + # TODO - Annotate + + # Reduce the number of lookups we need to do for the part categories + categories = {} + + for item in data: + + if item['image']: + item['image'] = os.path.join(settings.MEDIA_URL, item['image']) + + cat_id = item['category'] + + if cat_id: + if cat_id not in categories: + categories[cat_id] = PartCategory.objects.get(pk=cat_id).pathstring + + item['category__name'] = categories[cat_id] + else: + item['category__name'] = None + + + return Response(data) + + def get_queryset(self): # Does the user wish to filter by category? diff --git a/InvenTree/static/script/inventree/part.js b/InvenTree/static/script/inventree/part.js index d56a8d4769..83d21aa074 100644 --- a/InvenTree/static/script/inventree/part.js +++ b/InvenTree/static/script/inventree/part.js @@ -112,16 +112,25 @@ function loadPartTable(table, url, options={}) { } columns.push({ - field: 'full_name', + field: 'name', title: 'Part', sortable: true, formatter: function(value, row, index, field) { - if (row.is_template) { - value = '' + value + ''; + var name = ''; + + if (row.IPN) { + name += row.IPN; + name += ' | '; } - var display = imageHoverIcon(row.image_url) + renderLink(value, row.url); + name += value; + + if (row.is_template) { + name = '' + name + ''; + } + + var display = imageHoverIcon(row.image) + renderLink(name, '/part/' + row.pk + '/'); if (!row.active) { display = display + "INACTIVE"; @@ -146,11 +155,11 @@ function loadPartTable(table, url, options={}) { columns.push({ sortable: true, - field: 'category_name', + field: 'category__name', title: 'Category', formatter: function(value, row, index, field) { if (row.category) { - return renderLink(row.category_name, "/part/category/" + row.category + "/"); + return renderLink(row.category__name, "/part/category/" + row.category + "/"); } else { return ''; @@ -159,13 +168,13 @@ function loadPartTable(table, url, options={}) { }); columns.push({ - field: 'total_stock', + field: 'in_stock', title: 'Stock', searchable: false, sortable: true, formatter: function(value, row, index, field) { if (value) { - return renderLink(value, row.url + 'stock/'); + return renderLink(value, '/part/' + row.pk + '/stock/'); } else { return "No Stock";