2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-10-25 02:17:37 +00:00

move filtering of serializer fields out of functions into mixin

This commit is contained in:
Matthias Mair
2025-10-05 18:47:25 +02:00
parent 2e7e8d5eee
commit 3ee3cea8d8
5 changed files with 229 additions and 304 deletions

View File

@@ -28,6 +28,52 @@ from common.currency import currency_code_default, currency_code_mappings
from InvenTree.fields import InvenTreeRestURLField, InvenTreeURLField from InvenTree.fields import InvenTreeRestURLField, InvenTreeURLField
# region path filtering
class OptionalFilterabelSerializer(serializers.Serializer):
"""Mixin to add context to serializer."""
is_filterable = None
is_filterable_default = None
def __init__(self, *args, **kwargs):
"""Initialize the serializer."""
self.is_filterable = kwargs.pop('is_filterable', None)
self.is_filterable_default = kwargs.pop('is_filterable_default', True)
super().__init__(*args, **kwargs)
class PathScopedMixin:
"""Mixin to disable a serializer field based on kwargs passed to the view."""
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
flt_fld = {
k: a for k, a in self.fields.items() if getattr(a, 'is_filterable', None)
}
filter_targets = {k: kwargs.pop(k, False) for k in flt_fld}
super().__init__(*args, **kwargs)
if InvenTree.ready.isGeneratingSchema():
return
# Throw out fields which are not requested
for k, v in filter_targets.items():
if v is not True:
self.fields.pop(k, None)
# Decorator for marking serialzier fields that can be filtered out
def can_filter(func, default=False):
"""Decorator for marking serializer fields as filterable."""
func._kwargs['is_filterable'] = True
func._kwargs['is_filterable_default'] = default
return func
# endregion
class EmptySerializer(serializers.Serializer): class EmptySerializer(serializers.Serializer):
"""Empty serializer for use in testing.""" """Empty serializer for use in testing."""
@@ -222,7 +268,9 @@ class DependentField(serializers.Field):
return None return None
class InvenTreeModelSerializer(serializers.ModelSerializer): class InvenTreeModelSerializer(
OptionalFilterabelSerializer, serializers.ModelSerializer
):
"""Inherits the standard Django ModelSerializer class, but also ensures that the underlying model class data are checked on validation.""" """Inherits the standard Django ModelSerializer class, but also ensures that the underlying model class data are checked on validation."""
# Switch out URLField mapping # Switch out URLField mapping

View File

@@ -52,6 +52,8 @@ from InvenTree.serializers import (
InvenTreeModelSerializer, InvenTreeModelSerializer,
InvenTreeMoneySerializer, InvenTreeMoneySerializer,
NotesFieldMixin, NotesFieldMixin,
PathScopedMixin,
can_filter,
) )
from order.status_codes import ( from order.status_codes import (
PurchaseOrderStatusGroups, PurchaseOrderStatusGroups,
@@ -276,15 +278,6 @@ class AbstractExtraLineSerializer(
): ):
"""Abstract Serializer for a ExtraLine object.""" """Abstract Serializer for a ExtraLine object."""
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
order_detail = kwargs.pop('order_detail', False)
super().__init__(*args, **kwargs)
if order_detail is not True and not isGeneratingSchema():
self.fields.pop('order_detail', None)
quantity = serializers.FloatField() quantity = serializers.FloatField()
price = InvenTreeMoneySerializer(allow_null=True) price = InvenTreeMoneySerializer(allow_null=True)
@@ -343,15 +336,6 @@ class PurchaseOrderSerializer(
'order_currency': {'required': False}, 'order_currency': {'required': False},
} }
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
supplier_detail = kwargs.pop('supplier_detail', False)
super().__init__(*args, **kwargs)
if supplier_detail is not True and not isGeneratingSchema():
self.fields.pop('supplier_detail', None)
def skip_create_fields(self): def skip_create_fields(self):
"""Skip these fields when instantiating a new object.""" """Skip these fields when instantiating a new object."""
fields = super().skip_create_fields() fields = super().skip_create_fields()
@@ -389,8 +373,10 @@ class PurchaseOrderSerializer(
source='supplier.name', read_only=True, label=_('Supplier Name') source='supplier.name', read_only=True, label=_('Supplier Name')
) )
supplier_detail = CompanyBriefSerializer( supplier_detail = can_filter(
source='supplier', many=False, read_only=True, allow_null=True CompanyBriefSerializer(
source='supplier', many=False, read_only=True, allow_null=True
)
) )
@@ -522,20 +508,17 @@ class PurchaseOrderLineItemSerializer(
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer.""" """Initialization routine for the serializer."""
part_detail = kwargs.pop('part_detail', False) part_detail = kwargs.pop('part_detail', False)
order_detail = kwargs.pop('order_detail', False)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if isGeneratingSchema(): if isGeneratingSchema():
return return
# TODO INVE-T1 support complex filters
if part_detail is not True: if part_detail is not True:
self.fields.pop('part_detail', None) self.fields.pop('part_detail', None)
self.fields.pop('supplier_part_detail', None) self.fields.pop('supplier_part_detail', None)
if order_detail is not True:
self.fields.pop('order_detail', None)
def skip_create_fields(self): def skip_create_fields(self):
"""Return a list of fields to skip when creating a new object.""" """Return a list of fields to skip when creating a new object."""
return ['auto_pricing', 'merge_items', *super().skip_create_fields()] return ['auto_pricing', 'merge_items', *super().skip_create_fields()]
@@ -644,8 +627,10 @@ class PurchaseOrderLineItemSerializer(
help_text=_('Purchase price currency') help_text=_('Purchase price currency')
) )
order_detail = PurchaseOrderSerializer( order_detail = can_filter(
source='order', read_only=True, allow_null=True, many=False PurchaseOrderSerializer(
source='order', read_only=True, allow_null=True, many=False
)
) )
build_order_detail = build.serializers.BuildSerializer( build_order_detail = build.serializers.BuildSerializer(
@@ -724,8 +709,10 @@ class PurchaseOrderExtraLineSerializer(
): ):
"""Serializer for a PurchaseOrderExtraLine object.""" """Serializer for a PurchaseOrderExtraLine object."""
order_detail = PurchaseOrderSerializer( order_detail = can_filter(
source='order', many=False, read_only=True, allow_null=True PurchaseOrderSerializer(
source='order', many=False, read_only=True, allow_null=True
)
) )
class Meta(AbstractExtraLineMeta): class Meta(AbstractExtraLineMeta):
@@ -1009,15 +996,6 @@ class SalesOrderSerializer(
extra_kwargs = {'order_currency': {'required': False}} extra_kwargs = {'order_currency': {'required': False}}
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
customer_detail = kwargs.pop('customer_detail', False)
super().__init__(*args, **kwargs)
if customer_detail is not True and not isGeneratingSchema():
self.fields.pop('customer_detail', None)
def skip_create_fields(self): def skip_create_fields(self):
"""Skip these fields when instantiating a new object.""" """Skip these fields when instantiating a new object."""
fields = super().skip_create_fields() fields = super().skip_create_fields()
@@ -1058,8 +1036,10 @@ class SalesOrderSerializer(
return queryset return queryset
customer_detail = CompanyBriefSerializer( customer_detail = can_filter(
source='customer', many=False, read_only=True, allow_null=True CompanyBriefSerializer(
source='customer', many=False, read_only=True, allow_null=True
)
) )
shipments_count = serializers.IntegerField( shipments_count = serializers.IntegerField(
@@ -1083,6 +1063,7 @@ class SalesOrderIssueSerializer(OrderAdjustSerializer):
class SalesOrderLineItemSerializer( class SalesOrderLineItemSerializer(
DataImportExportSerializerMixin, DataImportExportSerializerMixin,
AbstractLineItemSerializer, AbstractLineItemSerializer,
PathScopedMixin,
InvenTreeModelSerializer, InvenTreeModelSerializer,
): ):
"""Serializer for a SalesOrderLineItem object.""" """Serializer for a SalesOrderLineItem object."""
@@ -1116,29 +1097,6 @@ class SalesOrderLineItemSerializer(
'on_order', 'on_order',
] ]
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer.
- Add extra related serializer information if required
"""
part_detail = kwargs.pop('part_detail', False)
order_detail = kwargs.pop('order_detail', False)
customer_detail = kwargs.pop('customer_detail', False)
super().__init__(*args, **kwargs)
if isGeneratingSchema():
return
if part_detail is not True:
self.fields.pop('part_detail', None)
if order_detail is not True:
self.fields.pop('order_detail', None)
if customer_detail is not True:
self.fields.pop('customer_detail', None)
@staticmethod @staticmethod
def annotate_queryset(queryset): def annotate_queryset(queryset):
"""Add some extra annotations to this queryset. """Add some extra annotations to this queryset.
@@ -1236,14 +1194,18 @@ class SalesOrderLineItemSerializer(
return queryset return queryset
order_detail = SalesOrderSerializer( order_detail = can_filter(
source='order', many=False, read_only=True, allow_null=True SalesOrderSerializer(
source='order', many=False, read_only=True, allow_null=True
)
) )
part_detail = PartBriefSerializer( part_detail = can_filter(
source='part', many=False, read_only=True, allow_null=True PartBriefSerializer(source='part', many=False, read_only=True, allow_null=True)
) )
customer_detail = CompanyBriefSerializer( customer_detail = can_filter(
source='order.customer', many=False, read_only=True, allow_null=True CompanyBriefSerializer(
source='order.customer', many=False, read_only=True, allow_null=True
)
) )
# Annotated fields # Annotated fields
@@ -1291,15 +1253,6 @@ class SalesOrderShipmentSerializer(NotesFieldMixin, InvenTreeModelSerializer):
'notes', 'notes',
] ]
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
order_detail = kwargs.pop('order_detail', True)
super().__init__(*args, **kwargs)
if not order_detail and not isGeneratingSchema():
self.fields.pop('order_detail', None)
@staticmethod @staticmethod
def annotate_queryset(queryset): def annotate_queryset(queryset):
"""Annotate the queryset with extra information.""" """Annotate the queryset with extra information."""
@@ -1314,8 +1267,11 @@ class SalesOrderShipmentSerializer(NotesFieldMixin, InvenTreeModelSerializer):
read_only=True, allow_null=True, label=_('Allocated Items') read_only=True, allow_null=True, label=_('Allocated Items')
) )
order_detail = SalesOrderSerializer( order_detail = can_filter(
source='order', read_only=True, allow_null=True, many=False SalesOrderSerializer(
source='order', read_only=True, allow_null=True, many=False
),
True,
) )
@@ -1352,34 +1308,6 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
read_only_fields = ['line', ''] read_only_fields = ['line', '']
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
order_detail = kwargs.pop('order_detail', False)
part_detail = kwargs.pop('part_detail', True)
item_detail = kwargs.pop('item_detail', True)
location_detail = kwargs.pop('location_detail', False)
customer_detail = kwargs.pop('customer_detail', False)
super().__init__(*args, **kwargs)
if isGeneratingSchema():
return
if not order_detail:
self.fields.pop('order_detail', None)
if not part_detail:
self.fields.pop('part_detail', None)
if not item_detail:
self.fields.pop('item_detail', None)
if not location_detail:
self.fields.pop('location_detail', None)
if not customer_detail:
self.fields.pop('customer_detail', None)
part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True) part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
order = serializers.PrimaryKeyRelatedField( order = serializers.PrimaryKeyRelatedField(
source='line.order', many=False, read_only=True source='line.order', many=False, read_only=True
@@ -1391,26 +1319,38 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
) )
# Extra detail fields # Extra detail fields
order_detail = SalesOrderSerializer( order_detail = can_filter(
source='line.order', many=False, read_only=True, allow_null=True SalesOrderSerializer(
source='line.order', many=False, read_only=True, allow_null=True
)
) )
part_detail = PartBriefSerializer( part_detail = can_filter(
source='item.part', many=False, read_only=True, allow_null=True PartBriefSerializer(
source='item.part', many=False, read_only=True, allow_null=True
),
True,
) )
item_detail = stock.serializers.StockItemSerializer( item_detail = can_filter(
source='item', stock.serializers.StockItemSerializer(
many=False, source='item',
read_only=True, many=False,
allow_null=True, read_only=True,
part_detail=False, allow_null=True,
location_detail=False, part_detail=False,
supplier_part_detail=False, location_detail=False,
supplier_part_detail=False,
),
True,
) )
location_detail = stock.serializers.LocationBriefSerializer( location_detail = can_filter(
source='item.location', many=False, read_only=True, allow_null=True stock.serializers.LocationBriefSerializer(
source='item.location', many=False, read_only=True, allow_null=True
)
) )
customer_detail = CompanyBriefSerializer( customer_detail = can_filter(
source='line.order.customer', many=False, read_only=True, allow_null=True CompanyBriefSerializer(
source='line.order.customer', many=False, read_only=True, allow_null=True
)
) )
shipment_detail = SalesOrderShipmentSerializer( shipment_detail = SalesOrderShipmentSerializer(
@@ -1860,8 +1800,10 @@ class SalesOrderExtraLineSerializer(
model = order.models.SalesOrderExtraLine model = order.models.SalesOrderExtraLine
order_detail = SalesOrderSerializer( order_detail = can_filter(
source='order', many=False, read_only=True, allow_null=True SalesOrderSerializer(
source='order', many=False, read_only=True, allow_null=True
)
) )
@@ -1891,15 +1833,6 @@ class ReturnOrderSerializer(
read_only_fields = ['creation_date'] read_only_fields = ['creation_date']
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
customer_detail = kwargs.pop('customer_detail', False)
super().__init__(*args, **kwargs)
if customer_detail is not True and not isGeneratingSchema():
self.fields.pop('customer_detail', None)
def skip_create_fields(self): def skip_create_fields(self):
"""Skip these fields when instantiating a new object.""" """Skip these fields when instantiating a new object."""
fields = super().skip_create_fields() fields = super().skip_create_fields()
@@ -1929,8 +1862,10 @@ class ReturnOrderSerializer(
return queryset return queryset
customer_detail = CompanyBriefSerializer( customer_detail = can_filter(
source='customer', many=False, read_only=True, allow_null=True CompanyBriefSerializer(
source='customer', many=False, read_only=True, allow_null=True
)
) )
@@ -2096,40 +2031,26 @@ class ReturnOrderLineItemSerializer(
'link', 'link',
] ]
def __init__(self, *args, **kwargs): order_detail = can_filter(
"""Initialization routine for the serializer.""" ReturnOrderSerializer(
order_detail = kwargs.pop('order_detail', False) source='order', many=False, read_only=True, allow_null=True
item_detail = kwargs.pop('item_detail', False) )
part_detail = kwargs.pop('part_detail', False)
super().__init__(*args, **kwargs)
if isGeneratingSchema():
return
if not order_detail:
self.fields.pop('order_detail', None)
if not item_detail:
self.fields.pop('item_detail', None)
if not part_detail:
self.fields.pop('part_detail', None)
order_detail = ReturnOrderSerializer(
source='order', many=False, read_only=True, allow_null=True
) )
quantity = serializers.FloatField( quantity = serializers.FloatField(
label=_('Quantity'), help_text=_('Quantity to return') label=_('Quantity'), help_text=_('Quantity to return')
) )
item_detail = stock.serializers.StockItemSerializer( item_detail = can_filter(
source='item', many=False, read_only=True, allow_null=True stock.serializers.StockItemSerializer(
source='item', many=False, read_only=True, allow_null=True
)
) )
part_detail = PartBriefSerializer( part_detail = can_filter(
source='item.part', many=False, read_only=True, allow_null=True PartBriefSerializer(
source='item.part', many=False, read_only=True, allow_null=True
)
) )
price = InvenTreeMoneySerializer(allow_null=True) price = InvenTreeMoneySerializer(allow_null=True)
@@ -2147,6 +2068,8 @@ class ReturnOrderExtraLineSerializer(
model = order.models.ReturnOrderExtraLine model = order.models.ReturnOrderExtraLine
order_detail = ReturnOrderSerializer( order_detail = can_filter(
source='order', many=False, read_only=True, allow_null=True ReturnOrderSerializer(
source='order', many=False, read_only=True, allow_null=True
)
) )

View File

@@ -34,6 +34,7 @@ import users.models
from importer.registry import register_importer from importer.registry import register_importer
from InvenTree.mixins import DataImportExportSerializerMixin from InvenTree.mixins import DataImportExportSerializerMixin
from InvenTree.ready import isGeneratingSchema from InvenTree.ready import isGeneratingSchema
from InvenTree.serializers import can_filter
from users.serializers import UserSerializer from users.serializers import UserSerializer
from .models import ( from .models import (
@@ -91,6 +92,7 @@ class CategorySerializer(
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# TODO INVE-T1 support complex filters
if not path_detail and not isGeneratingSchema(): if not path_detail and not isGeneratingSchema():
self.fields.pop('path', None) self.fields.pop('path', None)
@@ -355,6 +357,7 @@ class PartBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# TODO INVE-T1 support complex filters
if not pricing and not isGeneratingSchema(): if not pricing and not isGeneratingSchema():
self.fields.pop('pricing_min', None) self.fields.pop('pricing_min', None)
self.fields.pop('pricing_max', None) self.fields.pop('pricing_max', None)
@@ -414,25 +417,6 @@ class PartParameterSerializer(
read_only_fields = ['updated', 'updated_by'] read_only_fields = ['updated', 'updated_by']
def __init__(self, *args, **kwargs):
"""Custom initialization method for the serializer.
Allows us to optionally include or exclude particular information
"""
template_detail = kwargs.pop('template_detail', True)
part_detail = kwargs.pop('part_detail', False)
super().__init__(*args, **kwargs)
if isGeneratingSchema():
return
if not part_detail:
self.fields.pop('part_detail', None)
if not template_detail:
self.fields.pop('template_detail', None)
def save(self): def save(self):
"""Save the PartParameter instance.""" """Save the PartParameter instance."""
instance = super().save() instance = super().save()
@@ -444,12 +428,15 @@ class PartParameterSerializer(
return instance return instance
part_detail = PartBriefSerializer( part_detail = can_filter(
source='part', many=False, read_only=True, allow_null=True PartBriefSerializer(source='part', many=False, read_only=True, allow_null=True)
) )
template_detail = PartParameterTemplateSerializer( template_detail = can_filter(
source='template', many=False, read_only=True, allow_null=True PartParameterTemplateSerializer(
source='template', many=False, read_only=True, allow_null=True
),
True,
) )
updated_by_detail = UserSerializer( updated_by_detail = UserSerializer(
@@ -737,9 +724,7 @@ class PartSerializer(
- Allows us to optionally pass extra fields based on the query. - Allows us to optionally pass extra fields based on the query.
""" """
self.starred_parts = kwargs.pop('starred_parts', []) self.starred_parts = kwargs.pop('starred_parts', [])
category_detail = kwargs.pop('category_detail', False)
location_detail = kwargs.pop('location_detail', False) location_detail = kwargs.pop('location_detail', False)
parameters = kwargs.pop('parameters', False)
create = kwargs.pop('create', False) create = kwargs.pop('create', False)
pricing = kwargs.pop('pricing', True) pricing = kwargs.pop('pricing', True)
path_detail = kwargs.pop('path_detail', False) path_detail = kwargs.pop('path_detail', False)
@@ -749,15 +734,11 @@ class PartSerializer(
if isGeneratingSchema(): if isGeneratingSchema():
return return
if not category_detail: # TODO INVE-T1 support complex filters
self.fields.pop('category_detail', None)
if not location_detail: if not location_detail:
self.fields.pop('default_location_detail', None) self.fields.pop('default_location_detail', None)
if not parameters: # TODO INVE-T1 support complex filters
self.fields.pop('parameters', None)
if not path_detail: if not path_detail:
self.fields.pop('category_path', None) self.fields.pop('category_path', None)
@@ -769,6 +750,7 @@ class PartSerializer(
continue continue
self.fields.pop(f, None) self.fields.pop(f, None)
# TODO INVE-T1 support complex filters
if not pricing: if not pricing:
self.fields.pop('pricing_min', None) self.fields.pop('pricing_min', None)
self.fields.pop('pricing_max', None) self.fields.pop('pricing_max', None)
@@ -888,8 +870,10 @@ class PartSerializer(
return part in self.starred_parts return part in self.starred_parts
# Extra detail for the category # Extra detail for the category
category_detail = CategorySerializer( category_detail = can_filter(
source='category', many=False, read_only=True, allow_null=True CategorySerializer(
source='category', many=False, read_only=True, allow_null=True
)
) )
category_path = serializers.ListField( category_path = serializers.ListField(
@@ -1018,7 +1002,9 @@ class PartSerializer(
source='pricing_data.updated', allow_null=True, read_only=True source='pricing_data.updated', allow_null=True, read_only=True
) )
parameters = PartParameterSerializer(many=True, read_only=True, allow_null=True) parameters = can_filter(
PartParameterSerializer(many=True, read_only=True, allow_null=True)
)
# Extra fields used only for creation of a new Part instance # Extra fields used only for creation of a new Part instance
duplicate = DuplicatePartSerializer( duplicate = DuplicatePartSerializer(
@@ -1649,29 +1635,14 @@ class BomItemSerializer(
- part_detail and sub_part_detail serializers are only included if requested. - part_detail and sub_part_detail serializers are only included if requested.
- This saves a bunch of database requests - This saves a bunch of database requests
""" """
can_build = kwargs.pop('can_build', True)
part_detail = kwargs.pop('part_detail', False)
sub_part_detail = kwargs.pop('sub_part_detail', True)
pricing = kwargs.pop('pricing', True) pricing = kwargs.pop('pricing', True)
substitutes = kwargs.pop('substitutes', True)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if isGeneratingSchema(): if isGeneratingSchema():
return return
if not part_detail: # TODO INVE-T1 support complex filters
self.fields.pop('part_detail', None)
if not sub_part_detail:
self.fields.pop('sub_part_detail', None)
if not can_build:
self.fields.pop('can_build')
if not substitutes:
self.fields.pop('substitutes', None)
if not pricing: if not pricing:
self.fields.pop('pricing_min', None) self.fields.pop('pricing_min', None)
self.fields.pop('pricing_max', None) self.fields.pop('pricing_max', None)
@@ -1702,12 +1673,18 @@ class BomItemSerializer(
help_text=_('Select the parent assembly'), help_text=_('Select the parent assembly'),
) )
substitutes = BomItemSubstituteSerializer( substitutes = can_filter(
many=True, read_only=True, allow_null=True BomItemSubstituteSerializer(many=True, read_only=True, allow_null=True), True
) )
part_detail = PartBriefSerializer( part_detail = can_filter(
source='part', label=_('Assembly'), many=False, read_only=True, allow_null=True PartBriefSerializer(
source='part',
label=_('Assembly'),
many=False,
read_only=True,
allow_null=True,
)
) )
sub_part = serializers.PrimaryKeyRelatedField( sub_part = serializers.PrimaryKeyRelatedField(
@@ -1716,12 +1693,15 @@ class BomItemSerializer(
help_text=_('Select the component part'), help_text=_('Select the component part'),
) )
sub_part_detail = PartBriefSerializer( sub_part_detail = can_filter(
source='sub_part', PartBriefSerializer(
label=_('Component'), source='sub_part',
many=False, label=_('Component'),
read_only=True, many=False,
allow_null=True, read_only=True,
allow_null=True,
),
True,
) )
on_order = serializers.FloatField( on_order = serializers.FloatField(
@@ -1732,8 +1712,9 @@ class BomItemSerializer(
label=_('In Production'), read_only=True, allow_null=True label=_('In Production'), read_only=True, allow_null=True
) )
can_build = serializers.FloatField( can_build = can_filter(
label=_('Can Build'), read_only=True, allow_null=True serializers.FloatField(label=_('Can Build'), read_only=True, allow_null=True),
True,
) )
# Cached pricing fields # Cached pricing fields

View File

@@ -37,6 +37,8 @@ from InvenTree.serializers import (
InvenTreeCurrencySerializer, InvenTreeCurrencySerializer,
InvenTreeDecimalField, InvenTreeDecimalField,
InvenTreeModelSerializer, InvenTreeModelSerializer,
PathScopedMixin,
can_filter,
) )
from users.serializers import UserSerializer from users.serializers import UserSerializer
@@ -222,23 +224,9 @@ class StockItemTestResultSerializer(
read_only_fields = ['pk', 'user', 'date'] read_only_fields = ['pk', 'user', 'date']
def __init__(self, *args, **kwargs): user_detail = can_filter(
"""Add detail fields.""" UserSerializer(source='user', read_only=True, allow_null=True)
user_detail = kwargs.pop('user_detail', False) )
template_detail = kwargs.pop('template_detail', False)
super().__init__(*args, **kwargs)
if isGeneratingSchema():
return
if user_detail is not True:
self.fields.pop('user_detail', None)
if template_detail is not True:
self.fields.pop('template_detail', None)
user_detail = UserSerializer(source='user', read_only=True, allow_null=True)
template = serializers.PrimaryKeyRelatedField( template = serializers.PrimaryKeyRelatedField(
queryset=part_models.PartTestTemplate.objects.all(), queryset=part_models.PartTestTemplate.objects.all(),
@@ -249,8 +237,10 @@ class StockItemTestResultSerializer(
label=_('Test template for this result'), label=_('Test template for this result'),
) )
template_detail = part_serializers.PartTestTemplateSerializer( template_detail = can_filter(
source='template', read_only=True, allow_null=True part_serializers.PartTestTemplateSerializer(
source='template', read_only=True, allow_null=True
)
) )
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField( attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(
@@ -415,30 +405,14 @@ class StockItemSerializer(
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Add detail fields.""" """Add detail fields."""
part_detail = kwargs.pop('part_detail', True)
location_detail = kwargs.pop('location_detail', True)
supplier_part_detail = kwargs.pop('supplier_part_detail', True)
path_detail = kwargs.pop('path_detail', False) path_detail = kwargs.pop('path_detail', False)
tests = kwargs.pop('tests', False)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if isGeneratingSchema(): if isGeneratingSchema():
return return
if not part_detail: # TODO INVE-T1 support complex filters
self.fields.pop('part_detail', None)
if not location_detail:
self.fields.pop('location_detail', None)
if not supplier_part_detail:
self.fields.pop('supplier_part_detail', None)
if not tests:
self.fields.pop('tests', None)
if not path_detail: if not path_detail:
self.fields.pop('location_path', None) self.fields.pop('location_path', None)
@@ -613,32 +587,43 @@ class StockItemSerializer(
) )
# Optional detail fields, which can be appended via query parameters # Optional detail fields, which can be appended via query parameters
supplier_part_detail = company_serializers.SupplierPartSerializer( supplier_part_detail = can_filter(
label=_('Supplier Part'), company_serializers.SupplierPartSerializer(
source='supplier_part', label=_('Supplier Part'),
brief=True, source='supplier_part',
supplier_detail=False, brief=True,
manufacturer_detail=False, supplier_detail=False,
part_detail=False, manufacturer_detail=False,
many=False, part_detail=False,
read_only=True, many=False,
allow_null=True, read_only=True,
allow_null=True,
),
True,
) )
part_detail = part_serializers.PartBriefSerializer( part_detail = can_filter(
label=_('Part'), source='part', many=False, read_only=True, allow_null=True part_serializers.PartBriefSerializer(
label=_('Part'), source='part', many=False, read_only=True, allow_null=True
),
True,
) )
location_detail = LocationBriefSerializer( location_detail = can_filter(
label=_('Location'), LocationBriefSerializer(
source='location', label=_('Location'),
many=False, source='location',
read_only=True, many=False,
allow_null=True, read_only=True,
allow_null=True,
),
True,
) )
tests = StockItemTestResultSerializer( tests = can_filter(
source='test_results', many=True, read_only=True, allow_null=True StockItemTestResultSerializer(
source='test_results', many=True, read_only=True, allow_null=True
)
) )
quantity = InvenTreeDecimalField() quantity = InvenTreeDecimalField()
@@ -1189,6 +1174,7 @@ class LocationSerializer(
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# TODO INVE-T1 support complex filters
if not path_detail and not isGeneratingSchema(): if not path_detail and not isGeneratingSchema():
self.fields.pop('path', None) self.fields.pop('path', None)
@@ -1241,7 +1227,9 @@ class LocationSerializer(
@register_importer() @register_importer()
class StockTrackingSerializer( class StockTrackingSerializer(
DataImportExportSerializerMixin, InvenTree.serializers.InvenTreeModelSerializer DataImportExportSerializerMixin,
PathScopedMixin,
InvenTree.serializers.InvenTreeModelSerializer,
): ):
"""Serializer for StockItemTracking model.""" """Serializer for StockItemTracking model."""
@@ -1264,30 +1252,14 @@ class StockTrackingSerializer(
read_only_fields = ['date', 'user', 'label', 'tracking_type'] read_only_fields = ['date', 'user', 'label', 'tracking_type']
def __init__(self, *args, **kwargs):
"""Add detail fields."""
item_detail = kwargs.pop('item_detail', False)
user_detail = kwargs.pop('user_detail', False)
super().__init__(*args, **kwargs)
if isGeneratingSchema():
return
if item_detail is not True:
self.fields.pop('item_detail', None)
if user_detail is not True:
self.fields.pop('user_detail', None)
label = serializers.CharField(read_only=True) label = serializers.CharField(read_only=True)
item_detail = StockItemSerializer( item_detail = can_filter(
source='item', many=False, read_only=True, allow_null=True StockItemSerializer(source='item', many=False, read_only=True, allow_null=True)
) )
user_detail = UserSerializer( user_detail = can_filter(
source='user', many=False, read_only=True, allow_null=True UserSerializer(source='user', many=False, read_only=True, allow_null=True)
) )
deltas = serializers.JSONField(read_only=True) deltas = serializers.JSONField(read_only=True)

View File

@@ -245,6 +245,7 @@ class GroupSerializer(InvenTreeModelSerializer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Initialize this serializer with extra fields as required.""" """Initialize this serializer with extra fields as required."""
# TODO INVE-T1 support complex filters
role_detail = kwargs.pop('role_detail', False) role_detail = kwargs.pop('role_detail', False)
user_detail = kwargs.pop('user_detail', False) user_detail = kwargs.pop('user_detail', False)
permission_detail = kwargs.pop('permission_detail', False) permission_detail = kwargs.pop('permission_detail', False)