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

[refactor] Optional prefetch (#11012)

* Automatic prefetch of related fields for enable_filter

- Allows us to *not* prefetch fields (expensive) when they are not going to be used
- Enables re-usable components for common detail fields

* Refactor "project_code_detail" filter into common component

- Automatically apply correct prefetch fields

* Refactor 'parameters' annotation

- add 'enable_parameters_filter' function
- Prefetch parameters only when needed
- Refactor / consolidate code

* Refactor SupplierPartSerializer

- Make fields switchable
- Ensure correct prefetch_related

* Refactor serializer for ManufacturerPart

* Refactor BuildSerializer

* Refactor PurchaseOrderSerializer

* Refactor SalesOrderSerializer

* Refactor ReturnOrderSerializer

* Remove debug statements

* Tweaks

* Simplify custom filterable fields

* Bump API version

* Fix for data export

* Additional unit tests

* Remove unused "prefetch_func" option

* Refactor PurchaseOrderLineItemList

* Refactor SalesOrderLineItemList

* Refactor ReturnOrderLineItem

* Cleanup "pretty_name"

* Fix for build list

* Refactoring StockItem API endpoint

- Needs significant work still

* Refactoring for BuildLineSerializer

* Keep all optional fields when exporting data

* Improve "UserRoles" API endpoint

- Prefetch roles
- Prevents significant number of db hits

* Prefetch Parameter API list

* Bug fix for exporting logic

* Specify InvenTreeOutputOption

* Optional prefetch for primary_address

* Fix typing

* Fix unit test

* fixes for playwright tests

* Update Part API

- Improved prefetching

* Fix for prefetch
This commit is contained in:
Oliver
2025-12-16 14:46:17 +11:00
committed by GitHub
parent 1e120c3589
commit ba7b776257
25 changed files with 508 additions and 420 deletions

View File

@@ -10,7 +10,7 @@ from rest_framework import serializers
from sql_util.utils import SubqueryCount
from taggit.serializers import TagListSerializerField
import common.serializers
import common.filters
import company.filters
import part.filters
import part.serializers as part_serializers
@@ -163,22 +163,19 @@ class CompanySerializer(
queryset = queryset.annotate(parts_supplied=SubqueryCount('supplied_parts'))
queryset = queryset.prefetch_related(
return queryset
primary_address = enable_filter(
AddressBriefSerializer(read_only=True, allow_null=True),
False,
filter_name='address_detail',
prefetch_fields=[
Prefetch(
'addresses',
queryset=Address.objects.filter(primary=True),
to_attr='primary_address_list',
)
)
queryset = Company.annotate_parameters(queryset)
return queryset
primary_address = enable_filter(
AddressBriefSerializer(read_only=True, allow_null=True),
True,
filter_name='address_detail',
],
)
image = InvenTreeImageSerializerField(required=False, allow_null=True)
@@ -195,11 +192,7 @@ 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',
)
parameters = common.filters.enable_parameters_filter()
def save(self):
"""Save the Company instance."""
@@ -269,24 +262,14 @@ class ManufacturerPartSerializer(
tags = TagListSerializerField(required=False)
parameters = enable_filter(
common.serializers.ParameterSerializer(many=True, read_only=True),
False,
filter_name='parameters',
)
parameters = common.filters.enable_parameters_filter()
part_detail = enable_filter(
part_serializers.PartBriefSerializer(
source='part', many=False, read_only=True, allow_null=True
),
True,
)
manufacturer_detail = enable_filter(
CompanyBriefSerializer(
source='manufacturer', many=False, read_only=True, allow_null=True
),
True,
prefetch_fields=['part'],
)
pretty_name = enable_filter(
@@ -297,6 +280,14 @@ class ManufacturerPartSerializer(
queryset=Company.objects.filter(is_manufacturer=True)
)
manufacturer_detail = enable_filter(
CompanyBriefSerializer(
source='manufacturer', many=False, read_only=True, allow_null=True
),
True,
prefetch_fields=['manufacturer'],
)
class SupplierPriceBreakBriefSerializer(
FilterableSerializerMixin, InvenTreeModelSerializer
@@ -399,33 +390,13 @@ class SupplierPartSerializer(
# Check if 'available' quantity was supplied
self.has_available_quantity = 'available' in kwargs.get('data', {})
# TODO INVE-T1 support complex filters
brief = kwargs.pop('brief', False)
detail_default = not brief
part_detail = kwargs.pop('part_detail', detail_default)
supplier_detail = kwargs.pop('supplier_detail', detail_default)
manufacturer_detail = kwargs.pop('manufacturer_detail', detail_default)
prettify = kwargs.pop('pretty', False)
super().__init__(*args, **kwargs)
if isGeneratingSchema():
return
if part_detail is not True:
self.fields.pop('part_detail', None)
if supplier_detail is not True:
self.fields.pop('supplier_detail', None)
if manufacturer_detail is not True:
self.fields.pop('manufacturer_detail', None)
self.fields.pop('manufacturer_part_detail', None)
if brief or prettify is not True:
self.fields.pop('pretty_name', None)
if brief:
self.fields.pop('tags')
self.fields.pop('available')
@@ -455,46 +426,61 @@ class SupplierPartSerializer(
),
False,
filter_name='price_breaks',
prefetch_fields=['pricebreaks'],
)
parameters = enable_filter(
common.serializers.ParameterSerializer(many=True, read_only=True),
parameters = common.filters.enable_parameters_filter()
part_detail = enable_filter(
part_serializers.PartBriefSerializer(
label=_('Part'), source='part', many=False, read_only=True, allow_null=True
),
False,
filter_name='parameters',
prefetch_fields=['part'],
)
part_detail = part_serializers.PartBriefSerializer(
label=_('Part'), source='part', many=False, read_only=True, allow_null=True
supplier_detail = enable_filter(
CompanyBriefSerializer(
label=_('Supplier'),
source='supplier',
many=False,
read_only=True,
allow_null=True,
),
False,
prefetch_fields=['supplier'],
)
supplier_detail = CompanyBriefSerializer(
label=_('Supplier'),
source='supplier',
many=False,
read_only=True,
allow_null=True,
manufacturer_detail = enable_filter(
CompanyBriefSerializer(
label=_('Manufacturer'),
source='manufacturer_part.manufacturer',
many=False,
read_only=True,
allow_null=True,
),
False,
prefetch_fields=['manufacturer_part__manufacturer'],
)
manufacturer_detail = CompanyBriefSerializer(
label=_('Manufacturer'),
source='manufacturer_part.manufacturer',
many=False,
read_only=True,
allow_null=True,
pretty_name = enable_filter(
FilterableCharField(read_only=True, allow_null=True), filter_name='pretty'
)
pretty_name = serializers.CharField(read_only=True, allow_null=True)
supplier = serializers.PrimaryKeyRelatedField(
label=_('Supplier'), queryset=Company.objects.filter(is_supplier=True)
)
manufacturer_part_detail = ManufacturerPartSerializer(
label=_('Manufacturer Part'),
source='manufacturer_part',
part_detail=False,
read_only=True,
allow_null=True,
manufacturer_part_detail = enable_filter(
ManufacturerPartSerializer(
label=_('Manufacturer Part'),
source='manufacturer_part',
part_detail=False,
read_only=True,
allow_null=True,
),
False,
prefetch_fields=['manufacturer_part'],
)
MPN = serializers.CharField(
@@ -511,15 +497,13 @@ class SupplierPartSerializer(
Fields:
in_stock: Current stock quantity for each SupplierPart
"""
queryset = queryset.prefetch_related('part', 'pricebreaks')
queryset = queryset.annotate(in_stock=part.filters.annotate_total_stock())
queryset = queryset.annotate(
on_order=company.filters.annotate_on_order_quantity()
)
queryset = SupplierPart.annotate_parameters(queryset)
queryset = queryset.prefetch_related('supplier', 'manufacturer_part')
return queryset