2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-12-16 09:18:10 +00:00

Adds ContentTypeField

- Handles representation of content type
- Provides human-readable options
This commit is contained in:
Oliver Walters
2025-11-24 13:27:04 +00:00
parent 6c76b309bf
commit ccc3997518
4 changed files with 26 additions and 97 deletions

View File

@@ -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."""

View File

@@ -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."""

View File

@@ -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(

View File

@@ -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