2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-12-17 09:48:30 +00:00

[API] API refactoring (#11023)

* API refactoring

- Specify prefetch_fields for optional child serializers
- Ref: https://github.com/inventree/InvenTree/pull/11012/

* Fixes for unit tests
This commit is contained in:
Oliver
2025-12-16 21:13:28 +11:00
committed by GitHub
parent c78b03b6ff
commit 19239c8621
12 changed files with 44 additions and 31 deletions

View File

@@ -1,11 +1,15 @@
"""InvenTree API version information.""" """InvenTree API version information."""
# InvenTree API version # 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.""" """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 = """
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 v432 -> 2025-12-15 : https://github.com/inventree/InvenTree/pull/11012
- The "part_detail" field on the SupplierPart API endpoint is now optional - The "part_detail" field on the SupplierPart API endpoint is now optional
- The "supplier_detail" field on the SupplierPart API endpoint is now optional - The "supplier_detail" field on the SupplierPart API endpoint is now optional

View File

@@ -1435,6 +1435,7 @@ class BuildLineSerializer(
can_build=False, can_build=False,
), ),
False, False,
prefetch_fields=['bom_item'],
) )
assembly_detail = enable_filter( assembly_detail = enable_filter(

View File

@@ -893,9 +893,7 @@ class ParameterFilter(FilterSet):
class ParameterMixin: class ParameterMixin:
"""Mixin class for Parameter views.""" """Mixin class for Parameter views."""
queryset = common.models.Parameter.objects.all().prefetch_related( queryset = common.models.Parameter.objects.all().prefetch_related('model_type')
'model_type', 'updated_by', 'template', 'template__model_type'
)
serializer_class = common.serializers.ParameterSerializer serializer_class = common.serializers.ParameterSerializer
permission_classes = [IsAuthenticatedOrReadScope] permission_classes = [IsAuthenticatedOrReadScope]

View File

@@ -808,11 +808,15 @@ class ParameterSerializer(
) )
updated_by_detail = enable_filter( 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( 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'],
) )

View File

@@ -430,13 +430,6 @@ class SupplierPriceBreakMixin:
queryset = SupplierPriceBreak.objects.all() queryset = SupplierPriceBreak.objects.all()
serializer_class = SupplierPriceBreakSerializer 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): class SupplierPriceBreakOutputOptions(OutputConfiguration):
"""Available output options for the SupplierPriceBreak endpoints.""" """Available output options for the SupplierPriceBreak endpoints."""

View File

@@ -572,7 +572,9 @@ class SupplierPriceBreakSerializer(
supplier_detail = enable_filter( supplier_detail = enable_filter(
CompanyBriefSerializer( CompanyBriefSerializer(
source='part.supplier', many=False, read_only=True, allow_null=True source='part.supplier', many=False, read_only=True, allow_null=True
) ),
False,
prefetch_fields=['part__supplier'],
) )
part_detail = enable_filter( part_detail = enable_filter(
@@ -580,4 +582,5 @@ class SupplierPriceBreakSerializer(
source='part', brief=True, many=False, read_only=True, allow_null=True source='part', brief=True, many=False, read_only=True, allow_null=True
), ),
False, False,
prefetch_fields=['part', 'part__part', 'part__part__pricing_data'],
) )

View File

@@ -570,7 +570,7 @@ class PartPricingDetail(RetrieveUpdateAPI):
"""API endpoint for viewing part pricing data.""" """API endpoint for viewing part pricing data."""
serializer_class = part_serializers.PartPricingSerializer serializer_class = part_serializers.PartPricingSerializer
queryset = Part.objects.all() queryset = Part.objects.all().select_related('pricing_data')
def get_object(self): def get_object(self):
"""Return the PartPricing object associated with the linked Part.""" """Return the PartPricing object associated with the linked Part."""
@@ -1361,6 +1361,8 @@ class BomOutputOptions(OutputConfiguration):
InvenTreeOutputOption('can_build', default=True), InvenTreeOutputOption('can_build', default=True),
InvenTreeOutputOption('part_detail'), InvenTreeOutputOption('part_detail'),
InvenTreeOutputOption('sub_part_detail'), InvenTreeOutputOption('sub_part_detail'),
InvenTreeOutputOption('substitutes'),
InvenTreeOutputOption('pricing'),
] ]

View File

@@ -1563,7 +1563,15 @@ class BomItemSerializer(
) )
substitutes = enable_filter( 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( part_detail = enable_filter(
@@ -1684,9 +1692,7 @@ class BomItemSerializer(
'sub_part__stock_items', 'sub_part__stock_items',
'sub_part__stock_items__allocations', 'sub_part__stock_items__allocations',
'sub_part__stock_items__sales_order_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 # Annotate with the 'total pricing' information based on unit pricing and quantity
queryset = queryset.annotate( queryset = queryset.annotate(
@@ -1751,6 +1757,7 @@ class CategoryParameterTemplateSerializer(
source='template', many=False, read_only=True source='template', many=False, read_only=True
), ),
True, True,
prefetch_fields=['template'],
) )
category_detail = enable_filter( category_detail = enable_filter(
@@ -1758,6 +1765,7 @@ class CategoryParameterTemplateSerializer(
source='category', many=False, read_only=True, allow_null=True source='category', many=False, read_only=True, allow_null=True
), ),
True, True,
prefetch_fields=['category'],
) )

View File

@@ -2708,7 +2708,7 @@ class BomItemTest(InvenTreeAPITestCase):
"""Get the detail view for a single BomItem object.""" """Get the detail view for a single BomItem object."""
url = reverse('api-bom-item-detail', kwargs={'pk': 3}) 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 = [ expected_values = [
'allow_variants', 'allow_variants',
@@ -2882,6 +2882,7 @@ class BomItemTest(InvenTreeAPITestCase):
# The BomItem detail endpoint should now also reflect the substitute data # The BomItem detail endpoint should now also reflect the substitute data
data = self.get( data = self.get(
reverse('api-bom-item-detail', kwargs={'pk': bom_item.pk}), reverse('api-bom-item-detail', kwargs={'pk': bom_item.pk}),
data={'substitutes': True},
expected_code=200, expected_code=200,
).data ).data

View File

@@ -223,7 +223,8 @@ class StockItemTestResultSerializer(
read_only_fields = ['pk', 'user', 'date'] read_only_fields = ['pk', 'user', 'date']
user_detail = enable_filter( 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( template = serializers.PrimaryKeyRelatedField(
@@ -238,7 +239,8 @@ class StockItemTestResultSerializer(
template_detail = enable_filter( template_detail = enable_filter(
part_serializers.PartTestTemplateSerializer( part_serializers.PartTestTemplateSerializer(
source='template', read_only=True, allow_null=True source='template', read_only=True, allow_null=True
) ),
prefetch_fields=['template'],
) )
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField( attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(
@@ -1244,11 +1246,13 @@ class StockTrackingSerializer(
label = serializers.CharField(read_only=True) label = serializers.CharField(read_only=True)
item_detail = enable_filter( 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( 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) deltas = serializers.JSONField(read_only=True)

View File

@@ -291,13 +291,6 @@ class GroupMixin(SerializerContextMixin):
serializer_class = GroupSerializer serializer_class = GroupSerializer
permission_classes = [InvenTree.permissions.IsStaffOrReadOnlyScope] 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): class GroupOutputOptions(OutputConfiguration):
"""Holds all available output options for Group views.""" """Holds all available output options for Group views."""

View File

@@ -268,11 +268,13 @@ class GroupSerializer(FilterableSerializerMixin, InvenTreeModelSerializer):
source='rule_sets', many=True, read_only=True, allow_null=True source='rule_sets', many=True, read_only=True, allow_null=True
), ),
filter_name='role_detail', filter_name='role_detail',
prefetch_fields=['rule_sets'],
) )
users = enable_filter( users = enable_filter(
UserSerializer(source='user_set', many=True, read_only=True, allow_null=True), UserSerializer(source='user_set', many=True, read_only=True, allow_null=True),
filter_name='user_detail', filter_name='user_detail',
prefetch_fields=['user_set'],
) )