mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-16 09:18:10 +00:00
Add custom serializer field for ContentType with choices
This commit is contained in:
@@ -281,6 +281,25 @@ def getModelsWithMixin(mixin_class) -> list:
|
||||
return [x for x in db_models if x is not None and issubclass(x, mixin_class)]
|
||||
|
||||
|
||||
def getModelChoicesWithMixin(
|
||||
mixin_class, allow_null: bool = False
|
||||
) -> list[tuple[str, str]]:
|
||||
"""Return a list of model choices (app_label.model_name, verbose_name) for models that inherit from the given mixin class."""
|
||||
choices = []
|
||||
|
||||
if allow_null:
|
||||
choices.append((None, _('None')))
|
||||
|
||||
models = getModelsWithMixin(mixin_class)
|
||||
|
||||
for model in models:
|
||||
label = f'{model._meta.app_label}.{model._meta.model_name}'
|
||||
name = model._meta.verbose_name
|
||||
choices.append((label, name))
|
||||
|
||||
return choices
|
||||
|
||||
|
||||
def notify_responsible(
|
||||
instance,
|
||||
sender,
|
||||
|
||||
@@ -28,6 +28,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 .setting.storages import StorageBackends
|
||||
|
||||
@@ -716,3 +717,67 @@ class RemoteImageMixin(metaclass=serializers.SerializerMetaclass):
|
||||
raise ValidationError(_('Failed to download image from remote URL'))
|
||||
|
||||
return url
|
||||
|
||||
|
||||
class ContentTypeField(serializers.Field):
|
||||
"""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.
|
||||
|
||||
Additionally, a "mixin_class" can be supplied to the field, which will restrict the valid content types to only those models which inherit from the specified mixin.
|
||||
"""
|
||||
|
||||
mixin_class = None
|
||||
|
||||
def __init__(self, *args, mixin_class=None, **kwargs):
|
||||
"""Initialize the ContentTypeField.
|
||||
|
||||
Args:
|
||||
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)
|
||||
)
|
||||
|
||||
def to_representation(self, value):
|
||||
"""Convert ContentType instance to string representation."""
|
||||
return f'{value.app_label}.{value.model}'
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""Convert string representation back to ContentType instance."""
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
content_type = None
|
||||
|
||||
try:
|
||||
app_label, model = data.split('.')
|
||||
content_types = ContentType.objects.filter(app_label=app_label, model=model)
|
||||
|
||||
if content_types.exists() and content_types.count() == 1:
|
||||
# Try exact match first
|
||||
content_type = content_types.first()
|
||||
else:
|
||||
# Try lookup just on model name
|
||||
content_types = ContentType.objects.filter(model=model)
|
||||
if content_types.exists() and content_types.count() == 1:
|
||||
content_type = content_types.first()
|
||||
|
||||
except Exception:
|
||||
raise ValidationError(_('Invalid content type format'))
|
||||
|
||||
if content_type is None:
|
||||
raise ValidationError(_('Content type not found'))
|
||||
|
||||
if self.mixin_class is not None:
|
||||
model_class = content_type.model_class()
|
||||
if not issubclass(model_class, self.mixin_class):
|
||||
raise ValidationError(
|
||||
_('Content type does not match required mixin class')
|
||||
)
|
||||
|
||||
return content_type
|
||||
|
||||
Reference in New Issue
Block a user