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): def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs) queryset = super().get_queryset(*args, **kwargs)
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
@ -619,8 +618,6 @@ class PartList(generics.ListCreateAPIView):
def get_queryset(self, *args, **kwargs): def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs) queryset = super().get_queryset(*args, **kwargs)
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
@ -633,10 +630,6 @@ class PartList(generics.ListCreateAPIView):
params = self.request.query_params 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) queryset = super().filter_queryset(queryset)
# Filter by "uses" query - Limit to parts which use the provided part # 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 django_cleanup import cleanup
from mptt.models import TreeForeignKey, MPTTModel from mptt.models import TreeForeignKey, MPTTModel
from mptt.managers import TreeManager
from stdimage.models import StdImageField from stdimage.models import StdImageField
@ -284,6 +285,24 @@ def match_part_names(match, threshold=80, reverse=True, compare_length=False):
return matches 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 @cleanup.ignore
class Part(MPTTModel): class Part(MPTTModel):
""" The Part object represents an abstract part, the 'concept' of an actual entity. """ 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) responsible: User who is responsible for this part (optional)
""" """
objects = PartManager()
class Meta: class Meta:
verbose_name = _("Part") verbose_name = _("Part")
verbose_name_plural = _("Parts") verbose_name_plural = _("Parts")

View File

@ -215,25 +215,6 @@ class PartSerializer(InvenTreeModelSerializer):
if category_detail is not True: if category_detail is not True:
self.fields.pop('category_detail') 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 @staticmethod
def annotate_queryset(queryset): def annotate_queryset(queryset):
""" """

View File

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

View File

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

View File

@ -23,6 +23,7 @@ from django.dispatch import receiver
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
from mptt.models import MPTTModel, TreeForeignKey from mptt.models import MPTTModel, TreeForeignKey
from mptt.managers import TreeManager
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -130,6 +131,31 @@ def before_delete_stock_location(sender, instance, using, **kwargs):
child.save() 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): class StockItem(MPTTModel):
""" """
A StockItem object represents a quantity of physical instances of a part. 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 - 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 @staticmethod
def annotate_queryset(queryset): def annotate_queryset(queryset):
""" """