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:
parent
85fc709fc7
commit
dee519e848
@ -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
|
||||||
|
|
||||||
|
@ -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',
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user