From c1cd327fc223a5b5d023cc6d5b2647bb595afdb0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 10 Nov 2025 11:19:40 +0000 Subject: [PATCH] Update InvenTreeParameterMixin class - Fetch queryset of all linked Parameter instances - Ensure deletion of linked instances --- src/backend/InvenTree/InvenTree/models.py | 67 ++++++++++++++------- src/backend/InvenTree/common/models.py | 2 +- src/backend/InvenTree/common/serializers.py | 2 +- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/backend/InvenTree/InvenTree/models.py b/src/backend/InvenTree/InvenTree/models.py index 5c950a35f0..6648ab2fb9 100644 --- a/src/backend/InvenTree/InvenTree/models.py +++ b/src/backend/InvenTree/InvenTree/models.py @@ -466,15 +466,56 @@ class InvenTreeMetadataModel(MetadataMixin, InvenTreeModel): abstract = True -class InvenTreeParameterMixin: +class InvenTreePermissionCheckMixin: + """Provides an abstracted class for managing permissions against related fields.""" + + @classmethod + def check_related_permission(cls, permission, user) -> bool: + """Check if the user has permission to perform the specified action on the attachment. + + The default implementation runs a permission check against *this* model class, + but this can be overridden in the implementing class if required. + + Arguments: + permission: The permission to check (add / change / view / delete) + user: The user to check against + + Returns: + bool: True if the user has permission, False otherwise + """ + perm = f'{cls._meta.app_label}.{permission}_{cls._meta.model_name}' + return user.has_perm(perm) + + +class InvenTreeParameterMixin(InvenTreePermissionCheckMixin): """Provides an abstracted class for managing parameters. Links the implementing model to the common.models.Parameter table, and provides the following methods: """ + def delete(self, *args, **kwargs): + """Handle the deletion of a model instance. -class InvenTreeAttachmentMixin: + Before deleting the model instance, delete any associated parameters. + """ + self.parameters.all().delete() + super().delete(*args, **kwargs) + + @property + def parameters(self) -> QuerySet: + """Return a queryset containing all parameters for this model.""" + return self.parameters_for_model().filter(model_id=self.pk) + + def parameters_for_model(self) -> QuerySet: + """Return a QuerySet containing all parameters for this model class.""" + from common.models import Parameter + + model_type = self.__class__.__name__.lower() + return Parameter.objects.filter(model_type=model_type) + + +class InvenTreeAttachmentMixin(InvenTreePermissionCheckMixin): """Provides an abstracted class for managing file attachments. Links the implementing model to the common.models.Attachment table, @@ -492,33 +533,15 @@ class InvenTreeAttachmentMixin: super().delete(*args, **kwargs) @property - def attachments(self): + def attachments(self) -> QuerySet: """Return a queryset containing all attachments for this model.""" return self.attachments_for_model().filter(model_id=self.pk) - @classmethod - def check_attachment_permission(cls, permission, user) -> bool: - """Check if the user has permission to perform the specified action on the attachment. - - The default implementation runs a permission check against *this* model class, - but this can be overridden in the implementing class if required. - - Arguments: - permission: The permission to check (add / change / view / delete) - user: The user to check against - - Returns: - bool: True if the user has permission, False otherwise - """ - perm = f'{cls._meta.app_label}.{permission}_{cls._meta.model_name}' - return user.has_perm(perm) - - def attachments_for_model(self): + def attachments_for_model(self) -> QuerySet: """Return all attachments for this model class.""" from common.models import Attachment model_type = self.__class__.__name__.lower() - return Attachment.objects.filter(model_type=model_type) def create_attachment(self, attachment=None, link=None, comment='', **kwargs): diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index f100a777bf..8a702a0e40 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -2061,7 +2061,7 @@ class Attachment(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel if not issubclass(model_class, InvenTreeAttachmentMixin): raise ValidationError(_('Invalid model type specified for attachment')) - return model_class.check_attachment_permission(permission, user) + return model_class.check_related_permission(permission, user) class InvenTreeCustomUserStateModel(models.Model): diff --git a/src/backend/InvenTree/common/serializers.py b/src/backend/InvenTree/common/serializers.py index fd85fa287e..b28d20b37d 100644 --- a/src/backend/InvenTree/common/serializers.py +++ b/src/backend/InvenTree/common/serializers.py @@ -691,7 +691,7 @@ class AttachmentSerializer(InvenTreeModelSerializer): raise PermissionDenied(permission_error_msg) # Check that the user has the required permissions to attach files to the target model - if not target_model_class.check_attachment_permission('change', user): + if not target_model_class.check_related_permission('change', user): raise PermissionDenied(_(permission_error_msg)) return super().save(**kwargs)