From 8ed8c0deb5982ed5027704373a5c9caba5dd2b21 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:04:09 +1000 Subject: [PATCH] [bug] Attachment str fix (#12461) (#12462) * Fix stringify for Attachment model * Add regression tests (cherry picked from commit 6bd4842fd8de688418839e0b533ec4828f7acb68) Co-authored-by: Oliver --- 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 3e903ac9d2..2def2477ed 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -2034,9 +2034,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 2bd0c41db8..d00258e2a5 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."""