diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index a1e04531d4..81d0e5ba0b 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,15 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 432 +INVENTREE_API_VERSION = 433 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v433 -> 2025-12-16 : https://github.com/inventree/InvenTree/pull/11023 + - "substitutes" field on the BomItem API endpoint is now excluded by default + - Add "?substitutes=true" query parameter to include substitute parts in BomItem API endpoint(s) + v432 -> 2025-12-15 : https://github.com/inventree/InvenTree/pull/11012 - The "part_detail" field on the SupplierPart API endpoint is now optional - The "supplier_detail" field on the SupplierPart API endpoint is now optional diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index 97324bef95..19e01491a8 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -1435,6 +1435,7 @@ class BuildLineSerializer( can_build=False, ), False, + prefetch_fields=['bom_item'], ) assembly_detail = enable_filter( diff --git a/src/backend/InvenTree/common/api.py b/src/backend/InvenTree/common/api.py index ae972f6ed1..4118548acc 100644 --- a/src/backend/InvenTree/common/api.py +++ b/src/backend/InvenTree/common/api.py @@ -893,9 +893,7 @@ class ParameterFilter(FilterSet): class ParameterMixin: """Mixin class for Parameter views.""" - queryset = common.models.Parameter.objects.all().prefetch_related( - 'model_type', 'updated_by', 'template', 'template__model_type' - ) + queryset = common.models.Parameter.objects.all().prefetch_related('model_type') serializer_class = common.serializers.ParameterSerializer permission_classes = [IsAuthenticatedOrReadScope] diff --git a/src/backend/InvenTree/common/serializers.py b/src/backend/InvenTree/common/serializers.py index 49c777b476..b69593a559 100644 --- a/src/backend/InvenTree/common/serializers.py +++ b/src/backend/InvenTree/common/serializers.py @@ -808,11 +808,15 @@ class ParameterSerializer( ) updated_by_detail = enable_filter( - UserSerializer(source='updated_by', read_only=True, many=False), True + UserSerializer(source='updated_by', read_only=True, many=False), + True, + prefetch_fields=['updated_by'], ) template_detail = enable_filter( - ParameterTemplateSerializer(source='template', read_only=True, many=False), True + ParameterTemplateSerializer(source='template', read_only=True, many=False), + True, + prefetch_fields=['template', 'template__model_type'], ) diff --git a/src/backend/InvenTree/company/api.py b/src/backend/InvenTree/company/api.py index ba119ce8e6..f76cc3a7c8 100644 --- a/src/backend/InvenTree/company/api.py +++ b/src/backend/InvenTree/company/api.py @@ -430,13 +430,6 @@ class SupplierPriceBreakMixin: queryset = SupplierPriceBreak.objects.all() serializer_class = SupplierPriceBreakSerializer - def get_queryset(self): - """Return annotated queryset for the SupplierPriceBreak list endpoint.""" - queryset = super().get_queryset() - queryset = SupplierPriceBreakSerializer.annotate_queryset(queryset) - - return queryset - class SupplierPriceBreakOutputOptions(OutputConfiguration): """Available output options for the SupplierPriceBreak endpoints.""" diff --git a/src/backend/InvenTree/company/serializers.py b/src/backend/InvenTree/company/serializers.py index eb4dc0f229..d9b7782436 100644 --- a/src/backend/InvenTree/company/serializers.py +++ b/src/backend/InvenTree/company/serializers.py @@ -572,7 +572,9 @@ class SupplierPriceBreakSerializer( supplier_detail = enable_filter( CompanyBriefSerializer( source='part.supplier', many=False, read_only=True, allow_null=True - ) + ), + False, + prefetch_fields=['part__supplier'], ) part_detail = enable_filter( @@ -580,4 +582,5 @@ class SupplierPriceBreakSerializer( source='part', brief=True, many=False, read_only=True, allow_null=True ), False, + prefetch_fields=['part', 'part__part', 'part__part__pricing_data'], ) diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index aa18739cbc..612fc02654 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -570,7 +570,7 @@ class PartPricingDetail(RetrieveUpdateAPI): """API endpoint for viewing part pricing data.""" serializer_class = part_serializers.PartPricingSerializer - queryset = Part.objects.all() + queryset = Part.objects.all().select_related('pricing_data') def get_object(self): """Return the PartPricing object associated with the linked Part.""" @@ -1361,6 +1361,8 @@ class BomOutputOptions(OutputConfiguration): InvenTreeOutputOption('can_build', default=True), InvenTreeOutputOption('part_detail'), InvenTreeOutputOption('sub_part_detail'), + InvenTreeOutputOption('substitutes'), + InvenTreeOutputOption('pricing'), ] diff --git a/src/backend/InvenTree/part/serializers.py b/src/backend/InvenTree/part/serializers.py index 0f5ad566ae..125c140ba6 100644 --- a/src/backend/InvenTree/part/serializers.py +++ b/src/backend/InvenTree/part/serializers.py @@ -1563,7 +1563,15 @@ class BomItemSerializer( ) substitutes = enable_filter( - BomItemSubstituteSerializer(many=True, read_only=True, allow_null=True), True + BomItemSubstituteSerializer(many=True, read_only=True, allow_null=True), + False, + filter_name='substitutes', + prefetch_fields=[ + 'substitutes', + 'substitutes__part', + 'substitutes__part__stock_items', + 'substitutes__part__pricing_data', + ], ) part_detail = enable_filter( @@ -1684,9 +1692,7 @@ class BomItemSerializer( 'sub_part__stock_items', 'sub_part__stock_items__allocations', 'sub_part__stock_items__sales_order_allocations', - 'substitutes', - 'substitutes__part__stock_items', - ).select_related('part__pricing_data', 'sub_part__pricing_data') + ) # Annotate with the 'total pricing' information based on unit pricing and quantity queryset = queryset.annotate( @@ -1751,6 +1757,7 @@ class CategoryParameterTemplateSerializer( source='template', many=False, read_only=True ), True, + prefetch_fields=['template'], ) category_detail = enable_filter( @@ -1758,6 +1765,7 @@ class CategoryParameterTemplateSerializer( source='category', many=False, read_only=True, allow_null=True ), True, + prefetch_fields=['category'], ) diff --git a/src/backend/InvenTree/part/test_api.py b/src/backend/InvenTree/part/test_api.py index 2deb287f08..97f52a9d88 100644 --- a/src/backend/InvenTree/part/test_api.py +++ b/src/backend/InvenTree/part/test_api.py @@ -2708,7 +2708,7 @@ class BomItemTest(InvenTreeAPITestCase): """Get the detail view for a single BomItem object.""" url = reverse('api-bom-item-detail', kwargs={'pk': 3}) - response = self.get(url, expected_code=200) + response = self.get(url, {'substitutes': True}, expected_code=200) expected_values = [ 'allow_variants', @@ -2882,6 +2882,7 @@ class BomItemTest(InvenTreeAPITestCase): # The BomItem detail endpoint should now also reflect the substitute data data = self.get( reverse('api-bom-item-detail', kwargs={'pk': bom_item.pk}), + data={'substitutes': True}, expected_code=200, ).data diff --git a/src/backend/InvenTree/stock/serializers.py b/src/backend/InvenTree/stock/serializers.py index b6541db622..9a8111dbc2 100644 --- a/src/backend/InvenTree/stock/serializers.py +++ b/src/backend/InvenTree/stock/serializers.py @@ -223,7 +223,8 @@ class StockItemTestResultSerializer( read_only_fields = ['pk', 'user', 'date'] user_detail = enable_filter( - UserSerializer(source='user', read_only=True, allow_null=True) + UserSerializer(source='user', read_only=True, allow_null=True), + prefetch_fields=['user'], ) template = serializers.PrimaryKeyRelatedField( @@ -238,7 +239,8 @@ class StockItemTestResultSerializer( template_detail = enable_filter( part_serializers.PartTestTemplateSerializer( source='template', read_only=True, allow_null=True - ) + ), + prefetch_fields=['template'], ) attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField( @@ -1244,11 +1246,13 @@ class StockTrackingSerializer( label = serializers.CharField(read_only=True) item_detail = enable_filter( - StockItemSerializer(source='item', many=False, read_only=True, allow_null=True) + StockItemSerializer(source='item', many=False, read_only=True, allow_null=True), + prefetch_fields=['item'], ) user_detail = enable_filter( - UserSerializer(source='user', many=False, read_only=True, allow_null=True) + UserSerializer(source='user', many=False, read_only=True, allow_null=True), + prefetch_fields=['user'], ) deltas = serializers.JSONField(read_only=True) diff --git a/src/backend/InvenTree/users/api.py b/src/backend/InvenTree/users/api.py index 5cb0646719..42201ed095 100644 --- a/src/backend/InvenTree/users/api.py +++ b/src/backend/InvenTree/users/api.py @@ -291,13 +291,6 @@ class GroupMixin(SerializerContextMixin): serializer_class = GroupSerializer permission_classes = [InvenTree.permissions.IsStaffOrReadOnlyScope] - def get_queryset(self): - """Return queryset for this endpoint. - - Note that the queryset is filtered by the permissions of the current user. - """ - return super().get_queryset().prefetch_related('rule_sets', 'user_set') - class GroupOutputOptions(OutputConfiguration): """Holds all available output options for Group views.""" diff --git a/src/backend/InvenTree/users/serializers.py b/src/backend/InvenTree/users/serializers.py index 28b67204f9..0fdd481137 100644 --- a/src/backend/InvenTree/users/serializers.py +++ b/src/backend/InvenTree/users/serializers.py @@ -268,11 +268,13 @@ class GroupSerializer(FilterableSerializerMixin, InvenTreeModelSerializer): source='rule_sets', many=True, read_only=True, allow_null=True ), filter_name='role_detail', + prefetch_fields=['rule_sets'], ) users = enable_filter( UserSerializer(source='user_set', many=True, read_only=True, allow_null=True), filter_name='user_detail', + prefetch_fields=['user_set'], )