From 6bd4842fd8de688418839e0b533ec4828f7acb68 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 24 Jul 2026 12:30:40 +1000 Subject: [PATCH] [bug] Attachment str fix (#12461) * Fix stringify for Attachment model * Add regression tests --- src/backend/InvenTree/common/models.py | 7 +++-- src/backend/InvenTree/common/tests.py | 41 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index d03628897f..931630922c 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -2053,9 +2053,12 @@ class Attachment( def __str__(self): """Human name for attachment.""" - if self.attachment is not None: + if self.attachment and self.attachment.name: return os.path.basename(self.attachment.name) - return str(self.link) + elif self.link: + return str(self.link) + else: + return super().__str__() def validate_rename(self, filename: str): """Validate that the provided filename is valid, for renaming an attachment.""" diff --git a/src/backend/InvenTree/common/tests.py b/src/backend/InvenTree/common/tests.py index 6935e3b475..5589fb8eb6 100644 --- a/src/backend/InvenTree/common/tests.py +++ b/src/backend/InvenTree/common/tests.py @@ -2,6 +2,7 @@ import io import json +import os import time from datetime import timedelta from http import HTTPStatus @@ -222,6 +223,46 @@ class AttachmentTest(InvenTreeAPITestCase): self.assertIs(type(url), str) self.assertIn(f'/media/attachments/part/{part.pk}/test', url) + def test_str_representation(self): + """Test the __str__ method of the Attachment model. + + - If a file is attached, the string representation should be the file basename. + - If only a link is provided (no file), the string representation should be the link. + - If neither is set, fall back to the default django representation. + """ + part = Part.objects.first() + + # Case 1: Attachment has an uploaded file - string is the file basename + attachment = Attachment.objects.create( + attachment=self.generate_file('test.txt'), + comment='File attachment', + model_type='part', + model_id=part.pk, + ) + + self.assertTrue(str(attachment).startswith('test')) + self.assertTrue(str(attachment).endswith('.txt')) + self.assertEqual(str(attachment), os.path.basename(attachment.attachment.name)) + + # Case 2: Attachment has only a link (no uploaded file) - string is the link + link = 'https://www.example.org' + attachment = Attachment.objects.create( + link=link, comment='Link attachment', model_type='part', model_id=part.pk + ) + + self.assertEqual(str(attachment), link) + + # Case 3: Attachment has neither a file nor a link set + # (bypass 'save' to skip the validation which requires one of these fields) + attachment = Attachment( + comment='Empty attachment', model_type='part', model_id=part.pk + ) + + self.assertFalse(attachment.attachment) + self.assertFalse(attachment.link) + self.assertEqual(str(attachment), super(Attachment, attachment).__str__()) + self.assertEqual(str(attachment), f'Attachment object ({attachment.pk})') + class SettingsTest(InvenTreeTestCase): """Tests for the 'settings' model."""