2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-05-03 05:48:47 +00:00
InvenTree/InvenTree/common/serializers.py
Oliver 6d3978ea28
Add database model for defining custom units (#5268)
* Add database model for defining custom units

- Database model
- DRF serializer
- API endpoints

* Add validation hook

* Custom check for the 'definition' field

* Add settings page for custom units

- Table of units
- Create / edit / delete buttons

* Allow "unit" field to be empty

- Not actually required for custom unit definition

* Load custom unit definitions into global registry

* Docs: add core concepts page(s)

* Add some back links

* Update docs

* Add unit test for custom unit conversion

* More unit testing

* remove print statements

* Add missing table rule
2023-07-19 06:24:16 +10:00

314 lines
8.3 KiB
Python

"""JSON serializers for common components."""
from django.urls import reverse
from flags.state import flag_state
from rest_framework import serializers
import common.models as common_models
from InvenTree.helpers import get_objectreference
from InvenTree.helpers_model import construct_absolute_url
from InvenTree.serializers import (InvenTreeImageSerializerField,
InvenTreeModelSerializer)
class SettingsValueField(serializers.Field):
"""Custom serializer field for a settings value."""
def get_attribute(self, instance):
"""Return the object instance, not the attribute value."""
return instance
def to_representation(self, instance):
"""Return the value of the setting:
- Protected settings are returned as '***'
"""
return '***' if instance.protected else str(instance.value)
def to_internal_value(self, data):
"""Return the internal value of the setting"""
return str(data)
class SettingsSerializer(InvenTreeModelSerializer):
"""Base serializer for a settings object."""
key = serializers.CharField(read_only=True)
name = serializers.CharField(read_only=True)
description = serializers.CharField(read_only=True)
type = serializers.CharField(source='setting_type', read_only=True)
choices = serializers.SerializerMethodField()
model_name = serializers.CharField(read_only=True)
api_url = serializers.CharField(read_only=True)
value = SettingsValueField()
def get_choices(self, obj):
"""Returns the choices available for a given item."""
results = []
choices = obj.choices()
if choices:
for choice in choices:
results.append({
'value': choice[0],
'display_name': choice[1],
})
return results
class GlobalSettingsSerializer(SettingsSerializer):
"""Serializer for the InvenTreeSetting model."""
class Meta:
"""Meta options for GlobalSettingsSerializer."""
model = common_models.InvenTreeSetting
fields = [
'pk',
'key',
'value',
'name',
'description',
'type',
'choices',
'model_name',
'api_url',
'typ',
]
class UserSettingsSerializer(SettingsSerializer):
"""Serializer for the InvenTreeUserSetting model."""
class Meta:
"""Meta options for UserSettingsSerializer."""
model = common_models.InvenTreeUserSetting
fields = [
'pk',
'key',
'value',
'name',
'description',
'user',
'type',
'choices',
'model_name',
'api_url',
'typ',
]
user = serializers.PrimaryKeyRelatedField(read_only=True)
class GenericReferencedSettingSerializer(SettingsSerializer):
"""Serializer for a GenericReferencedSetting model.
Args:
MODEL: model class for the serializer
EXTRA_FIELDS: fields that need to be appended to the serializer
field must also be defined in the custom class
"""
MODEL = None
EXTRA_FIELDS = None
def __init__(self, *args, **kwargs):
"""Init overrides the Meta class to make it dynamic."""
class CustomMeta:
"""Scaffold for custom Meta class."""
fields = [
'pk',
'key',
'value',
'name',
'description',
'type',
'choices',
'model_name',
'api_url',
'typ',
]
# set Meta class
self.Meta = CustomMeta
self.Meta.model = self.MODEL
# extend the fields
self.Meta.fields.extend(self.EXTRA_FIELDS)
# resume operations
super().__init__(*args, **kwargs)
class NotificationMessageSerializer(InvenTreeModelSerializer):
"""Serializer for the InvenTreeUserSetting model."""
class Meta:
"""Meta options for NotificationMessageSerializer."""
model = common_models.NotificationMessage
fields = [
'pk',
'target',
'source',
'user',
'category',
'name',
'message',
'creation',
'age',
'age_human',
'read',
]
read_only_fields = [
'category',
'name',
'message',
'creation',
'age',
'age_human',
]
target = serializers.SerializerMethodField(read_only=True)
source = serializers.SerializerMethodField(read_only=True)
user = serializers.PrimaryKeyRelatedField(read_only=True)
read = serializers.BooleanField()
def get_target(self, obj):
"""Function to resolve generic object reference to target."""
target = get_objectreference(obj, 'target_content_type', 'target_object_id')
if target and 'link' not in target:
# Check if object has an absolute_url function
if hasattr(obj.target_object, 'get_absolute_url'):
target['link'] = obj.target_object.get_absolute_url()
else:
# check if user is staff - link to admin
request = self.context['request']
if request.user and request.user.is_staff:
meta = obj.target_object._meta
target['link'] = construct_absolute_url(reverse(
f'admin:{meta.db_table}_change',
kwargs={'object_id': obj.target_object_id}
))
return target
def get_source(self, obj):
"""Function to resolve generic object reference to source."""
return get_objectreference(obj, 'source_content_type', 'source_object_id')
class NewsFeedEntrySerializer(InvenTreeModelSerializer):
"""Serializer for the NewsFeedEntry model."""
class Meta:
"""Meta options for NewsFeedEntrySerializer."""
model = common_models.NewsFeedEntry
fields = [
'pk',
'feed_id',
'title',
'link',
'published',
'author',
'summary',
'read',
]
read = serializers.BooleanField()
class ConfigSerializer(serializers.Serializer):
"""Serializer for the InvenTree configuration.
This is a read-only serializer.
"""
def to_representation(self, instance):
"""Return the configuration data as a dictionary."""
if not isinstance(instance, str):
instance = list(instance.keys())[0]
return {'key': instance, **self.instance[instance]}
class NotesImageSerializer(InvenTreeModelSerializer):
"""Serializer for the NotesImage model."""
class Meta:
"""Meta options for NotesImageSerializer."""
model = common_models.NotesImage
fields = [
'pk',
'image',
'user',
'date',
]
read_only_fields = [
'date',
'user',
]
image = InvenTreeImageSerializerField(required=True)
class ProjectCodeSerializer(InvenTreeModelSerializer):
"""Serializer for the ProjectCode model."""
class Meta:
"""Meta options for ProjectCodeSerializer."""
model = common_models.ProjectCode
fields = [
'pk',
'code',
'description'
]
class FlagSerializer(serializers.Serializer):
"""Serializer for feature flags."""
def to_representation(self, instance):
"""Return the configuration data as a dictionary."""
request = self.context.get('request')
if not isinstance(instance, str):
instance = list(instance.keys())[0]
data = {'key': instance, 'state': flag_state(instance, request=request)}
if request and request.user.is_superuser:
data['conditions'] = self.instance[instance]
return data
class CustomUnitSerializer(InvenTreeModelSerializer):
"""DRF serializer for CustomUnit model."""
class Meta:
"""Meta options for CustomUnitSerializer."""
model = common_models.CustomUnit
fields = [
'pk',
'name',
'symbol',
'definition',
]