diff --git a/src/backend/InvenTree/InvenTree/serializers.py b/src/backend/InvenTree/InvenTree/serializers.py index 82d336127c..6218f0c458 100644 --- a/src/backend/InvenTree/InvenTree/serializers.py +++ b/src/backend/InvenTree/InvenTree/serializers.py @@ -7,6 +7,7 @@ from decimal import Decimal from typing import Any, Optional from django.conf import settings +from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError as DjangoValidationError from django.db import models from django.utils.translation import gettext_lazy as _ @@ -28,7 +29,7 @@ import InvenTree.ready from common.currency import currency_code_default, currency_code_mappings from InvenTree.fields import InvenTreeRestURLField, InvenTreeURLField from InvenTree.helpers import str2bool -from InvenTree.helpers_model import getModelChoicesWithMixin +from InvenTree.helpers_model import getModelsWithMixin from .setting.storages import StorageBackends @@ -719,7 +720,7 @@ class RemoteImageMixin(metaclass=serializers.SerializerMetaclass): return url -class ContentTypeField(serializers.Field): +class ContentTypeField(serializers.ChoiceField): """Serializer field which represents a ContentType as 'app_label.model_name'. This field converts a ContentType instance to a string representation in the format 'app_label.model_name' during serialization, and vice versa during deserialization. @@ -736,13 +737,26 @@ class ContentTypeField(serializers.Field): mixin_class: Optional mixin class to restrict valid content types. """ self.mixin_class = mixin_class - super().__init__(*args, **kwargs) # Override the 'choices' field, to limit to the appropriate models if self.mixin_class is not None: - self.choices = getModelChoicesWithMixin( - self.mixin_class, allow_null=kwargs.get('allow_null', False) - ) + models = getModelsWithMixin(self.mixin_class) + + kwargs['choices'] = [ + ( + f'{model._meta.app_label}.{model._meta.model_name}', + model._meta.verbose_name, + ) + for model in models + ] + else: + content_types = ContentType.objects.all() + + kwargs['choices'] = [ + (f'{ct.app_label}.{ct.model}', str(ct)) for ct in content_types + ] + + super().__init__(*args, **kwargs) def to_representation(self, value): """Convert ContentType instance to string representation.""" diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index ee9f2106d9..d050efb9ac 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -2386,11 +2386,6 @@ class ParameterTemplate( verbose_name = _('Parameter Template') verbose_name_plural = _('Parameter Templates') - class ModelChoices(RenderChoices): - """Model choices for parameter templates.""" - - choice_fnc = common.validators.parameter_template_model_options - @staticmethod def get_api_url() -> str: """Return the API URL associated with the ParameterTemplate model.""" diff --git a/src/backend/InvenTree/common/serializers.py b/src/backend/InvenTree/common/serializers.py index 5255c1886b..d218283e0a 100644 --- a/src/backend/InvenTree/common/serializers.py +++ b/src/backend/InvenTree/common/serializers.py @@ -21,7 +21,9 @@ from importer.registry import register_importer from InvenTree.helpers import get_objectreference from InvenTree.helpers_model import construct_absolute_url from InvenTree.mixins import DataImportExportSerializerMixin +from InvenTree.models import InvenTreeParameterMixin from InvenTree.serializers import ( + ContentTypeField, FilterableSerializerMixin, InvenTreeAttachmentSerializerField, InvenTreeImageSerializerField, @@ -721,22 +723,12 @@ class ParameterTemplateSerializer( 'enabled', ] - def __init__(self, *args, **kwargs): - """Override the model_type field to provide dynamic choices.""" - super().__init__(*args, **kwargs) - - if len(self.fields['model_type'].choices) == 0: - self.fields[ - 'model_type' - ].choices = common.validators.parameter_template_model_options() - # Note: The choices are overridden at run-time on class initialization - model_type = serializers.ChoiceField( + model_type = ContentTypeField( + mixin_class=InvenTreeParameterMixin, label=_('Model Type'), default='', - choices=common.validators.parameter_template_model_options(), required=False, - allow_blank=True, allow_null=True, ) @@ -767,15 +759,6 @@ class ParameterSerializer( read_only_fields = ['updated', 'updated_by'] - def __init__(self, *args, **kwargs): - """Override the model_type field to provide dynamic choices.""" - super().__init__(*args, **kwargs) - - if len(self.fields['model_type'].choices) == 0: - self.fields[ - 'model_type' - ].choices = common.validators.parameter_model_options() - def save(self, **kwargs): """Save the Parameter instance.""" from InvenTree.models import InvenTreeParameterMixin @@ -813,13 +796,11 @@ class ParameterSerializer( return instance # Note: The choices are overridden at run-time on class initialization - model_type = serializers.ChoiceField( + model_type = ContentTypeField( + mixin_class=InvenTreeParameterMixin, label=_('Model Type'), default='', - choices=common.validators.parameter_model_options(), required=False, - allow_blank=True, - allow_null=True, ) updated_by_detail = enable_filter( diff --git a/src/backend/InvenTree/common/validators.py b/src/backend/InvenTree/common/validators.py index 514ecc62dc..056bc4ca1b 100644 --- a/src/backend/InvenTree/common/validators.py +++ b/src/backend/InvenTree/common/validators.py @@ -9,67 +9,6 @@ import common.icons from common.settings import get_global_setting -def parameter_model_types(): - """Return a list of valid parameter model choices.""" - import InvenTree.models - - return list( - InvenTree.helpers_model.getModelsWithMixin( - InvenTree.models.InvenTreeParameterMixin - ) - ) - - -def parameter_model_options(): - """Return a list of options for models which support parameters.""" - return [ - (model.__name__.lower(), model._meta.verbose_name) - for model in parameter_model_types() - ] - - -def parameter_template_model_options(): - """Return a list of options for models which support parameter templates. - - Note: This includes a blank option at the start, since ParameterTemplate.model_type may be blank. - """ - return [('', _('Any model type')), *parameter_model_options()] - - -def parameter_model_class_from_label(label: str): - """Return the model class for the given label.""" - if not label: - raise ValidationError(_('No parameter model type provided')) - - for model in parameter_model_types(): - if model.__name__.lower() == label.lower(): - return model - - raise ValidationError(_('Invalid parameter model type') + f": '{label}'") - - -def validate_parameter_model_type(value: str): - """Ensure that the provided parameter model is valid.""" - model_names = [el[0] for el in parameter_model_options()] - if value not in model_names: - raise ValidationError('Model type does not support parameters') - - -def validate_parameter_template_model_type(value: str): - """Ensure that the provided model type is valid. - - Note: A ParameterTemplate may have a blank model type. - """ - value = str(value).strip() - - if not value: - # Empty values are allowed - return - - # Pass any other value to the Parameter model type validator - validate_parameter_model_type(value) - - def attachment_model_types(): """Return a list of valid attachment model choices.""" import InvenTree.models