2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 19:46:46 +00:00

[API] Improvements for API endpoints (#7794)

* Build allocation API updates

- Improve API query efficiency
- Add extra export fields to the BuildItemSerializer

* Remove commented code

* Improve query efficiency for BuildLine serializer

* Further improvements

* Improve StockList API endpoint

- Reduce from ~700ms to ~300ms with 250 results

* Improve query efficiency when fetching part parameter data

* Bump API version
This commit is contained in:
Oliver 2024-08-03 16:13:38 +10:00 committed by GitHub
parent 85fc709fc7
commit dee519e848
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 53 additions and 11 deletions

View File

@ -1,12 +1,15 @@
"""InvenTree API version information.""" """InvenTree API version information."""
# InvenTree API version # InvenTree API version
INVENTREE_API_VERSION = 230 INVENTREE_API_VERSION = 231
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" """Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """ INVENTREE_API_TEXT = """
v231 - 2024-08-03 : https://github.com/inventree/InvenTree/pull/7794
- Optimize BuildItem and BuildLine serializers to improve API efficiency
v230 - 2024-05-05 : https://github.com/inventree/InvenTree/pull/7164 v230 - 2024-05-05 : https://github.com/inventree/InvenTree/pull/7164
- Adds test statistics endpoint - Adds test statistics endpoint

View File

@ -575,19 +575,23 @@ class BuildItemList(DataExportViewMixin, BulkDeleteMixin, ListCreateAPI):
return self.serializer_class(*args, **kwargs) return self.serializer_class(*args, **kwargs)
def get_queryset(self): def get_queryset(self):
"""Override the queryset method, to allow filtering by stock_item.part.""" """Override the queryset method, to perform custom prefetch."""
queryset = super().get_queryset() queryset = super().get_queryset()
queryset = queryset.select_related( queryset = queryset.select_related(
'build_line', 'build_line',
'build_line__build', 'build_line__build',
'build_line__bom_item', 'build_line__bom_item',
'build_line__bom_item__part',
'build_line__bom_item__sub_part',
'install_into', 'install_into',
'stock_item', 'stock_item',
'stock_item__location', 'stock_item__location',
'stock_item__part', 'stock_item__part',
'stock_item__supplier_part', 'stock_item__supplier_part__part',
'stock_item__supplier_part__supplier',
'stock_item__supplier_part__manufacturer_part', 'stock_item__supplier_part__manufacturer_part',
'stock_item__supplier_part__manufacturer_part__manufacturer',
).prefetch_related( ).prefetch_related(
'stock_item__location__tags', 'stock_item__location__tags',
) )

View File

@ -23,7 +23,7 @@ from stock.status_codes import StockStatus
from stock.generators import generate_batch_code from stock.generators import generate_batch_code
from stock.models import StockItem, StockLocation from stock.models import StockItem, StockLocation
from stock.serializers import StockItemSerializerBrief, LocationSerializer from stock.serializers import StockItemSerializerBrief, LocationBriefSerializer
import common.models import common.models
from common.serializers import ProjectCodeSerializer from common.serializers import ProjectCodeSerializer
@ -1064,6 +1064,8 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
# These fields are only used for data export # These fields are only used for data export
export_only_fields = [ export_only_fields = [
'bom_part_id',
'bom_part_name',
'build_reference', 'build_reference',
'sku', 'sku',
'mpn', 'mpn',
@ -1071,9 +1073,11 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
'part_id', 'part_id',
'part_name', 'part_name',
'part_ipn', 'part_ipn',
'part_description',
'available_quantity', 'available_quantity',
'item_batch_code', 'item_batch_code',
'item_serial', 'item_serial',
'item_packaging',
] ]
class Meta: class Meta:
@ -1097,6 +1101,8 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
# The following fields are only used for data export # The following fields are only used for data export
'bom_reference', 'bom_reference',
'bom_part_id',
'bom_part_name',
'build_reference', 'build_reference',
'location_name', 'location_name',
'mpn', 'mpn',
@ -1104,9 +1110,11 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
'part_id', 'part_id',
'part_name', 'part_name',
'part_ipn', 'part_ipn',
'part_description',
'available_quantity', 'available_quantity',
'item_batch_code', 'item_batch_code',
'item_serial_number', 'item_serial_number',
'item_packaging',
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -1136,11 +1144,17 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
location_name = serializers.CharField(source='stock_item.location.name', label=_('Location Name'), read_only=True) location_name = serializers.CharField(source='stock_item.location.name', label=_('Location Name'), read_only=True)
build_reference = serializers.CharField(source='build.reference', label=_('Build Reference'), read_only=True) build_reference = serializers.CharField(source='build.reference', label=_('Build Reference'), read_only=True)
bom_reference = serializers.CharField(source='build_line.bom_item.reference', label=_('BOM Reference'), read_only=True) bom_reference = serializers.CharField(source='build_line.bom_item.reference', label=_('BOM Reference'), read_only=True)
item_packaging = serializers.CharField(source='stock_item.packaging', label=_('Packaging'), read_only=True)
# Part detail fields # Part detail fields
part_id = serializers.PrimaryKeyRelatedField(source='stock_item.part', label=_('Part ID'), many=False, read_only=True) part_id = serializers.PrimaryKeyRelatedField(source='stock_item.part', label=_('Part ID'), many=False, read_only=True)
part_name = serializers.CharField(source='stock_item.part.name', label=_('Part Name'), read_only=True) part_name = serializers.CharField(source='stock_item.part.name', label=_('Part Name'), read_only=True)
part_ipn = serializers.CharField(source='stock_item.part.IPN', label=_('Part IPN'), read_only=True) part_ipn = serializers.CharField(source='stock_item.part.IPN', label=_('Part IPN'), read_only=True)
part_description = serializers.CharField(source='stock_item.part.description', label=_('Part Description'), read_only=True)
# BOM Item Part ID (it may be different to the allocated part)
bom_part_id = serializers.PrimaryKeyRelatedField(source='build_line.bom_item.sub_part', label=_('BOM Part ID'), many=False, read_only=True)
bom_part_name = serializers.CharField(source='build_line.bom_item.sub_part.name', label=_('BOM Part Name'), read_only=True)
item_batch_code = serializers.CharField(source='stock_item.batch', label=_('Batch Code'), read_only=True) item_batch_code = serializers.CharField(source='stock_item.batch', label=_('Batch Code'), read_only=True)
item_serial_number = serializers.CharField(source='stock_item.serial', label=_('Serial Number'), read_only=True) item_serial_number = serializers.CharField(source='stock_item.serial', label=_('Serial Number'), read_only=True)
@ -1152,9 +1166,9 @@ class BuildItemSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
part_detail = part_serializers.PartBriefSerializer(source='stock_item.part', many=False, read_only=True, pricing=False) part_detail = part_serializers.PartBriefSerializer(source='stock_item.part', many=False, read_only=True, pricing=False)
stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True) stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True)
location = serializers.PrimaryKeyRelatedField(source='stock_item.location', many=False, read_only=True) location = serializers.PrimaryKeyRelatedField(source='stock_item.location', many=False, read_only=True)
location_detail = LocationSerializer(source='stock_item.location', read_only=True) location_detail = LocationBriefSerializer(source='stock_item.location', read_only=True)
build_detail = BuildSerializer(source='build_line.build', many=False, read_only=True) build_detail = BuildSerializer(source='build_line.build', many=False, read_only=True)
supplier_part_detail = company.serializers.SupplierPartSerializer(source='stock_item.supplier_part', many=False, read_only=True) supplier_part_detail = company.serializers.SupplierPartSerializer(source='stock_item.supplier_part', many=False, read_only=True, brief=True)
quantity = InvenTreeDecimalField(label=_('Allocated Quantity')) quantity = InvenTreeDecimalField(label=_('Allocated Quantity'))
available_quantity = InvenTreeDecimalField(source='stock_item.quantity', read_only=True, label=_('Available Quantity')) available_quantity = InvenTreeDecimalField(source='stock_item.quantity', read_only=True, label=_('Available Quantity'))
@ -1243,7 +1257,7 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
# Foreign key fields # Foreign key fields
bom_item_detail = part_serializers.BomItemSerializer(source='bom_item', many=False, read_only=True, pricing=False) bom_item_detail = part_serializers.BomItemSerializer(source='bom_item', many=False, read_only=True, pricing=False)
part_detail = part_serializers.PartSerializer(source='bom_item.sub_part', many=False, read_only=True, pricing=False) part_detail = part_serializers.PartBriefSerializer(source='bom_item.sub_part', many=False, read_only=True, pricing=False)
allocations = BuildItemSerializer(many=True, read_only=True) allocations = BuildItemSerializer(many=True, read_only=True)
# Annotated (calculated) fields # Annotated (calculated) fields
@ -1289,16 +1303,20 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
""" """
queryset = queryset.select_related( queryset = queryset.select_related(
'build', 'bom_item', 'build',
'bom_item',
'bom_item__part',
'bom_item__part__pricing_data',
'bom_item__sub_part',
'bom_item__sub_part__pricing_data',
) )
# Pre-fetch related fields # Pre-fetch related fields
queryset = queryset.prefetch_related( queryset = queryset.prefetch_related(
'bom_item__sub_part', 'bom_item__sub_part__tags',
'bom_item__sub_part__stock_items', 'bom_item__sub_part__stock_items',
'bom_item__sub_part__stock_items__allocations', 'bom_item__sub_part__stock_items__allocations',
'bom_item__sub_part__stock_items__sales_order_allocations', 'bom_item__sub_part__stock_items__sales_order_allocations',
'bom_item__sub_part__tags',
'bom_item__substitutes', 'bom_item__substitutes',
'bom_item__substitutes__part__stock_items', 'bom_item__substitutes__part__stock_items',
@ -1310,6 +1328,11 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
'allocations__stock_item__part', 'allocations__stock_item__part',
'allocations__stock_item__location', 'allocations__stock_item__location',
'allocations__stock_item__location__tags', 'allocations__stock_item__location__tags',
'allocations__stock_item__supplier_part',
'allocations__stock_item__supplier_part__part',
'allocations__stock_item__supplier_part__supplier',
'allocations__stock_item__supplier_part__manufacturer_part',
'allocations__stock_item__supplier_part__manufacturer_part__manufacturer',
) )
# Annotate the "allocated" quantity # Annotate the "allocated" quantity

View File

@ -381,9 +381,14 @@ class SupplierPartSerializer(
self.fields.pop('manufacturer_detail', None) self.fields.pop('manufacturer_detail', None)
self.fields.pop('manufacturer_part_detail', None) self.fields.pop('manufacturer_part_detail', None)
if prettify is not True: if brief or prettify is not True:
self.fields.pop('pretty_name', None) self.fields.pop('pretty_name', None)
if brief:
self.fields.pop('tags')
self.fields.pop('available')
self.fields.pop('availability_updated')
# Annotated field showing total in-stock quantity # Annotated field showing total in-stock quantity
in_stock = serializers.FloatField(read_only=True, label=_('In Stock')) in_stock = serializers.FloatField(read_only=True, label=_('In Stock'))

View File

@ -1195,6 +1195,10 @@ class PartMixin:
queryset = part_serializers.PartSerializer.annotate_queryset(queryset) queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
# Annotate with parameter template data?
if str2bool(self.request.query_params.get('parameters', False)):
queryset = queryset.prefetch_related('parameters', 'parameters__template')
return queryset return queryset
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):

View File

@ -506,7 +506,10 @@ class StockItemSerializer(
'part__category', 'part__category',
'part__pricing_data', 'part__pricing_data',
'supplier_part', 'supplier_part',
'supplier_part__part',
'supplier_part__supplier',
'supplier_part__manufacturer_part', 'supplier_part__manufacturer_part',
'supplier_part__manufacturer_part__manufacturer',
'supplier_part__tags', 'supplier_part__tags',
'test_results', 'test_results',
'tags', 'tags',