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";