diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index 5f47313629..4985687484 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 174 +INVENTREE_API_VERSION = 175 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v175 - 2024-02-21 : https://github.com/inventree/InvenTree/pull/6538 + - Adds "parts" count to PartParameterTemplate serializer + v174 - 2024-02-21 : https://github.com/inventree/InvenTree/pull/6536 - Expose PartCategory filters to the API documentation - Expose StockLocation filters to the API documentation diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 7ecec1a67b..258c16b940 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -1494,7 +1494,7 @@ class PartParameterTemplateFilter(rest_filters.FilterSet): model = PartParameterTemplate # Simple filter fields - fields = ['units', 'checkbox'] + fields = ['name', 'units', 'checkbox'] has_choices = rest_filters.BooleanFilter( method='filter_has_choices', label='Has Choice' @@ -1516,65 +1516,68 @@ class PartParameterTemplateFilter(rest_filters.FilterSet): return queryset.filter(Q(units=None) | Q(units='')).distinct() + part = rest_filters.ModelChoiceFilter( + queryset=Part.objects.all(), method='filter_part', label=_('Part') + ) -class PartParameterTemplateList(ListCreateAPI): + def filter_part(self, queryset, name, part): + """Filter queryset to include only PartParameterTemplates which are referenced by a part.""" + parameters = PartParameter.objects.filter(part=part) + template_ids = parameters.values_list('template').distinct() + return queryset.filter(pk__in=[el[0] for el in template_ids]) + + # Filter against a "PartCategory" - return only parameter templates which are referenced by parts in this category + category = rest_filters.ModelChoiceFilter( + queryset=PartCategory.objects.all(), + method='filter_category', + label=_('Category'), + ) + + def filter_category(self, queryset, name, category): + """Filter queryset to include only PartParameterTemplates which are referenced by parts in this category.""" + cats = category.get_descendants(include_self=True) + parameters = PartParameter.objects.filter(part__category__in=cats) + template_ids = parameters.values_list('template').distinct() + return queryset.filter(pk__in=[el[0] for el in template_ids]) + + +class PartParameterTemplateMixin: + """Mixin class for PartParameterTemplate API endpoints.""" + + queryset = PartParameterTemplate.objects.all() + serializer_class = part_serializers.PartParameterTemplateSerializer + + def get_queryset(self, *args, **kwargs): + """Return an annotated queryset for the PartParameterTemplateDetail endpoint.""" + queryset = super().get_queryset(*args, **kwargs) + + queryset = part_serializers.PartParameterTemplateSerializer.annotate_queryset( + queryset + ) + + return queryset + + +class PartParameterTemplateList(PartParameterTemplateMixin, ListCreateAPI): """API endpoint for accessing a list of PartParameterTemplate objects. - GET: Return list of PartParameterTemplate objects - POST: Create a new PartParameterTemplate object """ - queryset = PartParameterTemplate.objects.all() - serializer_class = part_serializers.PartParameterTemplateSerializer filterset_class = PartParameterTemplateFilter filter_backends = SEARCH_ORDER_FILTER - filterset_fields = ['name'] - search_fields = ['name', 'description'] - ordering_fields = ['name', 'units', 'checkbox'] - - def filter_queryset(self, queryset): - """Custom filtering for the PartParameterTemplate API.""" - queryset = super().filter_queryset(queryset) - - params = self.request.query_params - - # Filtering against a "Part" - return only parameter templates which are referenced by a part - part = params.get('part', None) - - if part is not None: - try: - part = Part.objects.get(pk=part) - parameters = PartParameter.objects.filter(part=part) - template_ids = parameters.values_list('template').distinct() - queryset = queryset.filter(pk__in=[el[0] for el in template_ids]) - except (ValueError, Part.DoesNotExist): - pass - - # Filtering against a "PartCategory" - return only parameter templates which are referenced by parts in this category - category = params.get('category', None) - - if category is not None: - try: - category = PartCategory.objects.get(pk=category) - cats = category.get_descendants(include_self=True) - parameters = PartParameter.objects.filter(part__category__in=cats) - template_ids = parameters.values_list('template').distinct() - queryset = queryset.filter(pk__in=[el[0] for el in template_ids]) - except (ValueError, PartCategory.DoesNotExist): - pass - - return queryset + ordering_fields = ['name', 'units', 'checkbox', 'parts'] -class PartParameterTemplateDetail(RetrieveUpdateDestroyAPI): +class PartParameterTemplateDetail(PartParameterTemplateMixin, RetrieveUpdateDestroyAPI): """API endpoint for accessing the detail view for a PartParameterTemplate object.""" - queryset = PartParameterTemplate.objects.all() - serializer_class = part_serializers.PartParameterTemplateSerializer + pass class PartParameterAPIMixin: diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 36ab1ba7a3..bbc8cd7b03 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -245,7 +245,18 @@ class PartParameterTemplateSerializer(InvenTree.serializers.InvenTreeModelSerial """Metaclass defining serializer fields.""" model = PartParameterTemplate - fields = ['pk', 'name', 'units', 'description', 'checkbox', 'choices'] + fields = ['pk', 'name', 'units', 'description', 'parts', 'checkbox', 'choices'] + + parts = serializers.IntegerField( + read_only=True, + label=_('Parts'), + help_text=_('Number of parts using this template'), + ) + + @staticmethod + def annotate_queryset(queryset): + """Annotate the queryset with the number of parts which use each parameter template.""" + return queryset.annotate(parts=SubqueryCount('instances')) class PartBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer): diff --git a/src/frontend/src/tables/part/PartParameterTemplateTable.tsx b/src/frontend/src/tables/part/PartParameterTemplateTable.tsx index ac5d0de818..cde98193de 100644 --- a/src/frontend/src/tables/part/PartParameterTemplateTable.tsx +++ b/src/frontend/src/tables/part/PartParameterTemplateTable.tsx @@ -51,6 +51,11 @@ export default function PartParameterTemplateTable() { sortable: true, switchable: false }, + { + accessor: 'parts', + sortable: true, + switchable: true + }, { accessor: 'units', sortable: true