Merge pull request #1850 from SchrodingersGat/api-speed

Api speed
This commit is contained in:
Oliver 2021-07-21 09:42:31 +10:00 committed by GitHub
commit 8eeb88a0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 48 additions and 52 deletions

View File

@ -361,7 +361,6 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = part_serializers.PartSerializer.prefetch_queryset(queryset)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
return queryset
@ -619,8 +618,6 @@ class PartList(generics.ListCreateAPIView):
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = part_serializers.PartSerializer.prefetch_queryset(queryset)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
return queryset
@ -633,10 +630,6 @@ class PartList(generics.ListCreateAPIView):
params = self.request.query_params
# Annotate calculated data to the queryset
# (This will be used for further filtering)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
queryset = super().filter_queryset(queryset)
# Filter by "uses" query - Limit to parts which use the provided part

View File

@ -27,6 +27,7 @@ from markdownx.models import MarkdownxField
from django_cleanup import cleanup
from mptt.models import TreeForeignKey, MPTTModel
from mptt.managers import TreeManager
from stdimage.models import StdImageField
@ -284,6 +285,24 @@ def match_part_names(match, threshold=80, reverse=True, compare_length=False):
return matches
class PartManager(TreeManager):
"""
Defines a custom object manager for the Part model.
The main purpose of this manager is to reduce the number of database hits,
as the Part model has a large number of ForeignKey fields!
"""
def get_queryset(self):
return super().get_queryset().prefetch_related(
'category',
'category__parent',
'stock_items',
'builds',
)
@cleanup.ignore
class Part(MPTTModel):
""" The Part object represents an abstract part, the 'concept' of an actual entity.
@ -321,6 +340,8 @@ class Part(MPTTModel):
responsible: User who is responsible for this part (optional)
"""
objects = PartManager()
class Meta:
verbose_name = _("Part")
verbose_name_plural = _("Parts")

View File

@ -215,25 +215,6 @@ class PartSerializer(InvenTreeModelSerializer):
if category_detail is not True:
self.fields.pop('category_detail')
@staticmethod
def prefetch_queryset(queryset):
"""
Prefetch related database tables,
to reduce database hits.
"""
return queryset.prefetch_related(
'category',
'category__parts',
'category__parent',
'stock_items',
'bom_items',
'builds',
'supplier_parts',
'supplier_parts__purchase_order_line_items',
'supplier_parts__purchase_order_line_items__order',
)
@staticmethod
def annotate_queryset(queryset):
"""

View File

@ -99,7 +99,7 @@ class CategoryTest(TestCase):
""" Test that the Category parameters are correctly fetched """
# Check number of SQL queries to iterate other parameters
with self.assertNumQueries(3):
with self.assertNumQueries(7):
# Prefetch: 3 queries (parts, parameters and parameters_template)
fasteners = self.fasteners.prefetch_parts_parameters()
# Iterate through all parts and parameters

View File

@ -84,7 +84,6 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = StockItemSerializer.prefetch_queryset(queryset)
queryset = StockItemSerializer.annotate_queryset(queryset)
return queryset
@ -637,7 +636,6 @@ class StockList(generics.ListCreateAPIView):
queryset = super().get_queryset(*args, **kwargs)
queryset = StockItemSerializer.prefetch_queryset(queryset)
queryset = StockItemSerializer.annotate_queryset(queryset)
return queryset

View File

@ -23,6 +23,7 @@ from django.dispatch import receiver
from markdownx.models import MarkdownxField
from mptt.models import MPTTModel, TreeForeignKey
from mptt.managers import TreeManager
from decimal import Decimal, InvalidOperation
from datetime import datetime, timedelta
@ -130,6 +131,31 @@ def before_delete_stock_location(sender, instance, using, **kwargs):
child.save()
class StockItemManager(TreeManager):
"""
Custom database manager for the StockItem class.
StockItem querysets will automatically prefetch related fields.
"""
def get_queryset(self):
return super().get_queryset().prefetch_related(
'belongs_to',
'build',
'customer',
'purchase_order',
'sales_order',
'supplier_part',
'supplier_part__supplier',
'allocations',
'sales_order_allocations',
'location',
'part',
'tracking_info'
)
class StockItem(MPTTModel):
"""
A StockItem object represents a quantity of physical instances of a part.

View File

@ -70,29 +70,6 @@ class StockItemSerializer(InvenTreeModelSerializer):
- Includes serialization for the item location
"""
@staticmethod
def prefetch_queryset(queryset):
"""
Prefetch related database tables,
to reduce database hits.
"""
return queryset.prefetch_related(
'belongs_to',
'build',
'customer',
'purchase_order',
'sales_order',
'supplier_part',
'supplier_part__supplier',
'supplier_part__manufacturer_part__manufacturer',
'allocations',
'sales_order_allocations',
'location',
'part',
'tracking_info',
)
@staticmethod
def annotate_queryset(queryset):
"""