Compare commits

...
1 Commits
Author SHA1 Message Date
shamoon a9e3ba4f69 Fix: ensure remove inbox tag children on remove_inbox_tags 2026-09-09 22:11:23 -07:00
2 changed files with 54 additions and 9 deletions
+15 -9
View File
@@ -1256,20 +1256,26 @@ class DocumentSerializer(
if "tags" in validated_data if "tags" in validated_data
else [] else []
) )
inbox_tags_not_being_added = Tag.objects.filter(is_inbox_tag=True).exclude( tags_being_added = Tag.objects.filter(id__in=tag_ids_being_added)
id__in=tag_ids_being_added, required_by_add_tags = set(tags_being_added)
) for tag in tags_being_added:
required_by_add_tags.update(tag.get_ancestors())
# Remove its descendants too, except any that is being added in this same update
tags_to_remove = set()
for tag in Tag.objects.filter(is_inbox_tag=True):
if tag in required_by_add_tags:
continue
tags_to_remove.add(tag)
tags_to_remove.update(tag.get_descendants())
if "tags" in validated_data: if "tags" in validated_data:
validated_data["tags"] = [ validated_data["tags"] = [
tag tag for tag in validated_data["tags"] if tag not in tags_to_remove
for tag in validated_data["tags"]
if tag not in inbox_tags_not_being_added
] ]
else: else:
validated_data["tags"] = [ validated_data["tags"] = [
tag tag for tag in instance.tags.all() if tag not in tags_to_remove
for tag in instance.tags.all()
if tag not in inbox_tags_not_being_added
] ]
if settings.AUDIT_LOG_ENABLED: if settings.AUDIT_LOG_ENABLED:
+39
View File
@@ -2,6 +2,7 @@ from unittest import mock
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.auth.models import User from django.contrib.auth.models import User
from rest_framework import status
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from documents import bulk_edit from documents import bulk_edit
@@ -108,6 +109,44 @@ class TestTagHierarchy(DirectoriesMixin, APITestCase):
self.document.refresh_from_db() self.document.refresh_from_db()
assert self.document.tags.count() == 0 assert self.document.tags.count() == 0
def test_remove_inbox_tags_removes_nested_children(self) -> None:
inbox = Tag.objects.create(name="Inbox", is_inbox_tag=True)
nested = Tag.objects.create(name="Nested", tn_parent=inbox)
self.document.add_nested_tags([nested])
resp = self.client.patch(
f"/api/documents/{self.document.pk}/",
{"title": "new title", "remove_inbox_tags": True},
format="json",
)
assert resp.status_code == status.HTTP_200_OK
self.document.refresh_from_db()
assert self.document.tags.count() == 0
# A subsequent save must not re-add the inbox tag as an ancestor
resp = self.client.patch(
f"/api/documents/{self.document.pk}/",
{"title": "another title", "tags": [], "remove_inbox_tags": True},
format="json",
)
assert resp.status_code == status.HTTP_200_OK
self.document.refresh_from_db()
assert self.document.tags.count() == 0
def test_remove_inbox_tags_keeps_inbox_when_nested_child_added(self) -> None:
inbox = Tag.objects.create(name="Inbox", is_inbox_tag=True)
nested = Tag.objects.create(name="Nested", tn_parent=inbox)
self.document.add_nested_tags([inbox])
self.client.patch(
f"/api/documents/{self.document.pk}/",
{"tags": [nested.pk], "remove_inbox_tags": True},
format="json",
)
self.document.refresh_from_db()
tags = set(self.document.tags.values_list("pk", flat=True))
assert tags == {inbox.pk, nested.pk}
def test_bulk_edit_respects_hierarchy(self) -> None: def test_bulk_edit_respects_hierarchy(self) -> None:
bulk_edit.add_tag([self.document.pk], self.child.pk) bulk_edit.add_tag([self.document.pk], self.child.pk)
self.document.refresh_from_db() self.document.refresh_from_db()