API / serializer updates

This commit is contained in:
Oliver Walters 2026-05-18 14:17:20 +00:00
parent 176733d762
commit 1078475acb
5 changed files with 159 additions and 1 deletions

View File

@ -819,6 +819,47 @@ class AttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
return super().destroy(request, *args, **kwargs)
class NoteFilter(FilterSet):
"""Filterset class for the NoteList API endpoint."""
class Meta:
"""Metaclass options for the filterset."""
model = common.models.Note
fields = ['model_type', 'model_id', 'updated_by']
model_type = rest_filters.CharFilter(method='filter_model_type', label='Model Type')
def filter_model_type(self, queryset, name, value):
"""Filter queryset to include only Parameters of the given model type."""
return common.filters.filter_content_type(
queryset, 'model_type', value, allow_null=False
)
class NoteMixin:
"""Mixin class for the Note views."""
queryset = common.models.Note.objects.all()
serializer_class = common.serializers.NoteSerializer
permission_classes = [IsAuthenticatedOrReadScope]
class NoteList(NoteMixin, ListCreateAPI):
"""List API endpoint for Note objects."""
filter_backends = SEARCH_ORDER_FILTER
filterset_class = NoteFilter
ordering_fields = ['model_id', 'model_type', 'user', 'creation']
search_fields = ['content', 'model_id', 'model_type', 'user__username']
unique_create_fields = ['model_type', 'model_id']
class NoteDetail(NoteMixin, RetrieveUpdateDestroyAPI):
"""Detail API endpoint for Note objects."""
class ParameterTemplateFilter(FilterSet):
"""FilterSet class for the ParameterTemplateList API endpoint."""
@ -1477,6 +1518,20 @@ common_api_urls = [
path('', AttachmentList.as_view(), name='api-attachment-list'),
]),
),
# Notes
path(
'note/',
include([
path(
'<int:pk>/',
include([
meta_path(common.models.Note),
path('', NoteDetail.as_view(), name='api-note-detail'),
]),
),
path('', NoteList.as_view(), name='api-note-list'),
]),
),
# Parameters and templates
path(
'parameter/',

View File

@ -1,4 +1,4 @@
# Generated by Django 5.2.14 on 2026-05-18 13:55
# Generated by Django 5.2.14 on 2026-05-18 14:16
import InvenTree.models
import django.db.models.deletion
@ -62,6 +62,12 @@ class Migration(migrations.Migration):
verbose_name="Description",
),
),
(
"content",
models.TextField(
blank=True, help_text="Note content", verbose_name="Content"
),
),
(
"model_type",
models.ForeignKey(

View File

@ -2987,6 +2987,10 @@ class Note(
help_text=_('Optional description field'),
)
content = models.TextField(
blank=True, verbose_name=_('Content'), help_text=_('Note content')
)
class BarcodeScanResult(InvenTree.models.InvenTreeModel):
"""Model for storing barcode scans results."""

View File

@ -809,6 +809,82 @@ class AttachmentSerializer(FilterableSerializerMixin, InvenTreeModelSerializer):
return super().save(**kwargs)
class NoteSerializer(InvenTreeModelSerializer):
"""Serializer for the Note model."""
class Meta:
"""Meta options for NoteSerializer."""
model = common_models.Note
fields = [
'pk',
'model_type',
'model_id',
'title',
'description',
'content',
'updated',
'updated_by',
]
read_only_fields = ['updated', 'updated_by']
def save(self, **kwargs):
"""Save the Note instance."""
from InvenTree.models import InvenTreeNoteMixin
from users.permissions import check_user_permission
model_type = self.validated_data.get('model_type', None)
if model_type is None and self.instance:
model_type = self.instance.model_type
# Ensure that the user has permission to modify notes for the specified model
user = self.context.get('request').user
target_model_class = model_type.model_class()
if not issubclass(target_model_class, InvenTreeNoteMixin):
raise PermissionDenied(_('Invalid model type specified for note'))
permission_error_msg = _(
'User does not have permission to create or edit notes for this model'
)
if not check_user_permission(user, target_model_class, 'change'):
raise PermissionDenied(permission_error_msg)
if not target_model_class.check_related_permission('change', user):
raise PermissionDenied(permission_error_msg)
instance = super().save(**kwargs)
instance.updated_by = user
instance.save()
return instance
# Note: The choices are overridden at run-time on class initialization
model_type = ContentTypeField(
mixin_class=InvenTreeParameterMixin,
choices=common.validators.note_model_options,
label=_('Model Type'),
default='',
allow_null=False,
)
updated_by_detail = OptionalField(
serializer_class=UserSerializer,
serializer_kwargs={
'source': 'updated_by',
'read_only': True,
'allow_null': True,
'many': False,
},
default_include=True,
prefetch_fields=['updated_by'],
)
@register_importer()
class ParameterTemplateSerializer(
DataImportExportSerializerMixin, InvenTreeModelSerializer

View File

@ -9,6 +9,23 @@ import common.icons
from common.settings import get_global_setting
def note_model_types():
"""Return a list of valid note model choices."""
import InvenTree.models
return list(
InvenTree.helpers_model.getModelsWithMixin(InvenTree.models.InvenTreeNotesMixin)
)
def note_model_options():
"""Return a list of options for models which support notes."""
return [
(model.__name__.lower(), model._meta.verbose_name)
for model in note_model_types()
]
def parameter_model_types():
"""Return a list of valid parameter model choices."""
import InvenTree.models