Significant increase in query speed for Part list

- Custom list method
- Cache PartCategory objects in memory
This commit is contained in:
Oliver Walters 2020-05-02 09:49:05 +10:00
parent acea0d6e92
commit 4a60da67fd
2 changed files with 63 additions and 7 deletions

View File

@ -110,7 +110,7 @@ class PartThumbs(generics.ListAPIView):
serializer_class = part_serializers.PartThumbSerializer serializer_class = part_serializers.PartThumbSerializer
def list(self, reguest, *args, **kwargs): def list(self, request, *args, **kwargs):
""" """
Serialize the available Part images. Serialize the available Part images.
- Images may be used for multiple parts! - Images may be used for multiple parts!
@ -142,6 +142,7 @@ class PartDetail(generics.RetrieveUpdateAPIView):
queryset = part_serializers.PartSerializer.prefetch_queryset(queryset) queryset = part_serializers.PartSerializer.prefetch_queryset(queryset)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset) queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
return queryset return queryset
permission_classes = [ permission_classes = [
@ -151,15 +152,13 @@ class PartDetail(generics.RetrieveUpdateAPIView):
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
try: try:
cat_detail = str2bool(self.request.query_params.get('category_detail', False)) kwargs['category_detail'] = str2bool(self.request.query_params.get('category_detail', False))
except AttributeError: except AttributeError:
cat_detail = None pass
# Ensure the request context is passed through # Ensure the request context is passed through
kwargs['context'] = self.get_serializer_context() kwargs['context'] = self.get_serializer_context()
kwargs['category_detail'] = cat_detail
# Pass a list of "starred" parts fo the current user to the serializer # Pass a list of "starred" parts fo the current user to the serializer
# We do this to reduce the number of database queries required! # We do this to reduce the number of database queries required!
if self.starred_parts is None and self.request is not None: if self.starred_parts is None and self.request is not None:
@ -206,8 +205,6 @@ class PartList(generics.ListCreateAPIView):
# Ensure the request context is passed through # Ensure the request context is passed through
kwargs['context'] = self.get_serializer_context() kwargs['context'] = self.get_serializer_context()
kwargs['category_detail'] = cat_detail
# Pass a list of "starred" parts fo the current user to the serializer # Pass a list of "starred" parts fo the current user to the serializer
# We do this to reduce the number of database queries required! # We do this to reduce the number of database queries required!
if self.starred_parts is None and self.request is not None: if self.starred_parts is None and self.request is not None:
@ -217,6 +214,63 @@ class PartList(generics.ListCreateAPIView):
return self.serializer_class(*args, **kwargs) 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)
return self.get_paginated_response(serializer.data)
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 categorie we need to query
category_ids = set()
for part in data:
cat_id = part['category']
if cat_id is not None:
category_ids.add(part['category'])
# 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[part['category']]
else:
detail = None
part['category_detail'] = detail
return Response(data)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
""" Override the default 'create' behaviour: """ Override the default 'create' behaviour:
We wish to save the user who created this part! We wish to save the user who created this part!

View File

@ -101,6 +101,8 @@ class PartSerializer(InvenTreeModelSerializer):
return queryset.prefetch_related( return queryset.prefetch_related(
'category', 'category',
'category__parts',
'category__parent',
'stock_items', 'stock_items',
'bom_items', 'bom_items',
'builds', 'builds',