From 5b4a8d50e6594f92790dc9a72b1af0efcdfef2be Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 25 Nov 2025 01:31:51 +0000 Subject: [PATCH] Directly annotate parameters against different model serializers --- src/backend/InvenTree/company/api.py | 37 ++++++++---- src/backend/InvenTree/company/serializers.py | 27 +++++++++ src/backend/InvenTree/part/serializers.py | 63 +++----------------- 3 files changed, 61 insertions(+), 66 deletions(-) diff --git a/src/backend/InvenTree/company/api.py b/src/backend/InvenTree/company/api.py index d8b3315dc4..2862a9fb76 100644 --- a/src/backend/InvenTree/company/api.py +++ b/src/backend/InvenTree/company/api.py @@ -168,9 +168,28 @@ class ManufacturerOutputOptions(OutputConfiguration): ] +class ManufacturerPartMixin(SerializerContextMixin): + """Mixin class for ManufacturerPart API endpoints.""" + + queryset = ManufacturerPart.objects.all() + serializer_class = ManufacturerPartSerializer + + def get_queryset(self, *args, **kwargs): + """Return annotated queryset for the ManufacturerPart list endpoint.""" + queryset = super().get_queryset(*args, **kwargs) + + queryset = queryset.prefetch_related( + 'part', 'manufacturer', 'supplier_parts', 'tags' + ) + + queryset = ManufacturerPart.annotate_parameters(queryset) + + return queryset + + class ManufacturerPartList( + ManufacturerPartMixin, SerializerContextMixin, - DataExportViewMixin, OutputOptionsMixin, ListCreateDestroyAPIView, ): @@ -180,13 +199,10 @@ class ManufacturerPartList( - POST: Create a new ManufacturerPart object """ - queryset = ManufacturerPart.objects.all().prefetch_related( - 'part', 'manufacturer', 'supplier_parts', 'tags' - ) - serializer_class = ManufacturerPartSerializer filterset_class = ManufacturerPartFilter - output_options = ManufacturerOutputOptions filter_backends = SEARCH_ORDER_FILTER + output_options = ManufacturerOutputOptions + search_fields = [ 'manufacturer__name', 'description', @@ -199,7 +215,9 @@ class ManufacturerPartList( ] -class ManufacturerPartDetail(RetrieveUpdateDestroyAPI): +class ManufacturerPartDetail( + ManufacturerPartMixin, OutputOptionsMixin, RetrieveUpdateDestroyAPI +): """API endpoint for detail view of ManufacturerPart object. - GET: Retrieve detail view @@ -207,9 +225,6 @@ class ManufacturerPartDetail(RetrieveUpdateDestroyAPI): - DELETE: Delete object """ - queryset = ManufacturerPart.objects.all() - serializer_class = ManufacturerPartSerializer - class SupplierPartFilter(FilterSet): """API filters for the SupplierPartList endpoint.""" @@ -310,7 +325,7 @@ class SupplierPartMixin: serializer_class = SupplierPartSerializer def get_queryset(self, *args, **kwargs): - """Return annotated queryest object for the SupplierPart list.""" + """Return annotated queryset object for the SupplierPart list.""" queryset = super().get_queryset(*args, **kwargs) queryset = SupplierPartSerializer.annotate_queryset(queryset) diff --git a/src/backend/InvenTree/company/serializers.py b/src/backend/InvenTree/company/serializers.py index f6ce2ff3f3..509120a7f6 100644 --- a/src/backend/InvenTree/company/serializers.py +++ b/src/backend/InvenTree/company/serializers.py @@ -11,6 +11,7 @@ from rest_framework import serializers from sql_util.utils import SubqueryCount from taggit.serializers import TagListSerializerField +import common.serializers import company.filters import part.filters import part.serializers as part_serializers @@ -112,6 +113,7 @@ class AddressBriefSerializer(InvenTreeModelSerializer): @register_importer() class CompanySerializer( + FilterableSerializerMixin, DataImportExportSerializerMixin, NotesFieldMixin, RemoteImageMixin, @@ -151,6 +153,7 @@ class CompanySerializer( 'address_count', 'primary_address', 'tax_id', + 'parameters', ] @staticmethod @@ -173,6 +176,8 @@ class CompanySerializer( ) ) + queryset = Company.annotate_parameters(queryset) + return queryset address = serializers.SerializerMethodField( @@ -211,6 +216,12 @@ class CompanySerializer( help_text=_('Default currency used for this supplier'), required=True ) + parameters = enable_filter( + common.serializers.ParameterSerializer(many=True, read_only=True), + False, + filter_name='parameters', + ) + def save(self): """Save the Company instance.""" super().save() @@ -274,10 +285,17 @@ class ManufacturerPartSerializer( 'barcode_hash', 'notes', 'tags', + 'parameters', ] tags = TagListSerializerField(required=False) + parameters = enable_filter( + common.serializers.ParameterSerializer(many=True, read_only=True), + False, + filter_name='parameters', + ) + part_detail = enable_filter( part_serializers.PartBriefSerializer( source='part', many=False, read_only=True, allow_null=True @@ -387,6 +405,7 @@ class SupplierPartSerializer( 'part_detail', 'tags', 'price_breaks', + 'parameters', ] read_only_fields = [ 'availability_updated', @@ -459,6 +478,12 @@ class SupplierPartSerializer( filter_name='price_breaks', ) + parameters = enable_filter( + common.serializers.ParameterSerializer(many=True, read_only=True), + False, + filter_name='parameters', + ) + part_detail = part_serializers.PartBriefSerializer( label=_('Part'), source='part', many=False, read_only=True, allow_null=True ) @@ -515,6 +540,8 @@ class SupplierPartSerializer( on_order=company.filters.annotate_on_order_quantity() ) + queryset = SupplierPart.annotate_parameters(queryset) + return queryset def update(self, supplier_part, data): diff --git a/src/backend/InvenTree/part/serializers.py b/src/backend/InvenTree/part/serializers.py index 2a8b62fa52..6fb5fec207 100644 --- a/src/backend/InvenTree/part/serializers.py +++ b/src/backend/InvenTree/part/serializers.py @@ -22,6 +22,7 @@ from sql_util.utils import SubqueryCount from taggit.serializers import TagListSerializerField import common.currency +import common.serializers import company.models import InvenTree.helpers import InvenTree.serializers @@ -394,60 +395,6 @@ class PartBriefSerializer( ) -@register_importer() -class PartParameterSerializer( - InvenTree.serializers.FilterableSerializerMixin, - DataImportExportSerializerMixin, - InvenTree.serializers.InvenTreeModelSerializer, -): - """JSON serializers for the PartParameter model.""" - - class Meta: - """Metaclass defining serializer fields.""" - - model = PartParameter - fields = [ - 'pk', - 'part', - 'part_detail', - 'template', - 'template_detail', - 'data', - 'data_numeric', - 'note', - 'updated', - 'updated_by', - 'updated_by_detail', - ] - read_only_fields = ['updated', 'updated_by'] - - def save(self): - """Save the PartParameter instance.""" - instance = super().save() - - if request := self.context.get('request', None): - # If the request is provided, update the 'updated_by' field - instance.updated_by = request.user - instance.save() - - return instance - - part_detail = enable_filter( - PartBriefSerializer(source='part', many=False, read_only=True, allow_null=True) - ) - - template_detail = enable_filter( - PartParameterTemplateSerializer( - source='template', many=False, read_only=True, allow_null=True - ), - True, - ) - - updated_by_detail = UserSerializer( - source='updated_by', many=False, read_only=True, allow_null=True - ) - - class DuplicatePartSerializer(serializers.Serializer): """Serializer for specifying options when duplicating a Part. @@ -771,6 +718,8 @@ class PartSerializer( """ queryset = queryset.prefetch_related('category', 'default_location') + queryset = Part.annotate_parameters(queryset) + # Annotate with the total number of revisions queryset = queryset.annotate(revision_count=SubqueryCount('revisions')) @@ -1010,7 +959,11 @@ class PartSerializer( ) parameters = enable_filter( - PartParameterSerializer(many=True, read_only=True, allow_null=True) + common.serializers.ParameterSerializer( + many=True, read_only=True, allow_null=True + ), + False, + filter_name='parameters', ) price_breaks = enable_filter(