From cb0b7209ec2dea6319d448576c3dc3fd68a0d824 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 20 Jul 2021 22:12:01 +1000 Subject: [PATCH] Add custom "list" function back in - Actually does make a significant difference to query speed --- InvenTree/part/api.py | 74 ++++++++++++++++++++++++++++++++++++---- InvenTree/part/models.py | 3 +- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 8c7258c4ee..7506bc09f4 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -536,15 +536,75 @@ class PartList(generics.ListCreateAPIView): kwargs['starred_parts'] = self.starred_parts - try: - params = self.request.query_params - - kwargs['category_detail'] = str2bool(params.get('category_detail', False)) - except AttributeError: - pass - return self.serializer_class(*args, **kwargs) + def list(self, request, *args, **kwargs): + """ + Overide the 'list' method, as the PartCategory objects are + very expensive to serialize! + + So we will serialize them first, and keep them in memory, + so that they do not have to be serialized multiple times... + """ + + queryset = self.filter_queryset(self.get_queryset()) + + page = self.paginate_queryset(queryset) + + if page is not None: + serializer = self.get_serializer(page, many=True) + else: + serializer = self.get_serializer(queryset, many=True) + + data = serializer.data + + # Do we wish to include PartCategory detail? + if str2bool(request.query_params.get('category_detail', False)): + + # Work out which part categories we need to query + category_ids = set() + + for part in data: + cat_id = part['category'] + + if cat_id is not None: + category_ids.add(cat_id) + + # Fetch only the required PartCategory objects from the database + categories = PartCategory.objects.filter(pk__in=category_ids).prefetch_related( + 'parts', + 'parent', + 'children', + ) + + category_map = {} + + # Serialize each PartCategory object + for category in categories: + category_map[category.pk] = part_serializers.CategorySerializer(category).data + + for part in data: + cat_id = part['category'] + + if cat_id is not None and cat_id in category_map.keys(): + detail = category_map[cat_id] + else: + detail = None + + part['category_detail'] = detail + + """ + Determine the response type based on the request. + a) For HTTP requests (e.g. via the browseable API) return a DRF response + b) For AJAX requests, simply return a JSON rendered response. + """ + if page is not None: + return self.get_paginated_response(data) + elif request.is_ajax(): + return JsonResponse(data, safe=False) + else: + return Response(data) + def perform_create(self, serializer): """ We wish to save the user who created this part! diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 95a8e407b9..fa19f8b075 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -296,8 +296,9 @@ class PartManager(models.Manager): return super().get_queryset().prefetch_related( 'category', + 'category__parent', 'stock_items', - 'builds', + 'builds', )