🐛(back) keep info if document has deleted children

With the soft delete feature, relying on the is_leaf method from the
treebeard is not accurate anymore. To determine if a node is a leaf, it
checks if the number of numchild is equal to 0. But a node can have soft
deleted children, then numchild is equal to 0, but it is not a leaf
because if we want to add a child we have to look for the last child to
compute a correct path. Otherwise we will have an error saying that the
path already exists.
This commit is contained in:
Nathan Panchout
2025-03-24 08:51:42 +01:00
parent ef7cc67387
commit 12f4a72f5e
3 changed files with 70 additions and 1 deletions
@@ -0,0 +1,17 @@
# Generated by Django 5.1.7 on 2025-03-14 14:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0019_alter_user_language_default_to_null"),
]
operations = [
migrations.AddField(
model_name="document",
name="has_deleted_children",
field=models.BooleanField(default=False),
),
]
+9 -1
View File
@@ -486,6 +486,7 @@ class Document(MP_Node, BaseModel):
)
deleted_at = models.DateTimeField(null=True, blank=True)
ancestors_deleted_at = models.DateTimeField(null=True, blank=True)
has_deleted_children = models.BooleanField(default=False)
_content = None
@@ -546,6 +547,12 @@ class Document(MP_Node, BaseModel):
content_file = ContentFile(bytes_content)
default_storage.save(file_key, content_file)
def is_leaf(self):
"""
:returns: True if the node is has no children
"""
return not self.has_deleted_children and self.numchild == 0
@property
def key_base(self):
"""Key base of the location where the document is stored in object storage."""
@@ -903,7 +910,8 @@ class Document(MP_Node, BaseModel):
if self.depth > 1:
self._meta.model.objects.filter(pk=self.get_parent().pk).update(
numchild=models.F("numchild") - 1
numchild=models.F("numchild") - 1,
has_deleted_children=True,
)
# Mark all descendants as soft deleted
@@ -1297,3 +1297,47 @@ def test_models_documents_restore_complex_bis(django_assert_num_queries):
def test_models_documents_get_select_options(ancestors_links, select_options):
"""Validate that the "get_select_options" method operates as expected."""
assert models.LinkReachChoices.get_select_options(ancestors_links) == select_options
def test_models_documents_children_create_after_sibling_deletion():
"""
It should be possible to create a new child after all children have been deleted.
"""
root = factories.DocumentFactory()
assert root.numchild == 0
assert root.has_deleted_children is False
assert root.is_leaf() is True
child1 = factories.DocumentFactory(parent=root)
child2 = factories.DocumentFactory(parent=root)
root.refresh_from_db()
assert root.numchild == 2
assert root.has_deleted_children is False
assert root.is_leaf() is False
child1.soft_delete()
child2.soft_delete()
root.refresh_from_db()
assert root.numchild == 0
assert root.has_deleted_children is True
assert root.is_leaf() is False
factories.DocumentFactory(parent=root)
root.refresh_from_db()
assert root.numchild == 1
assert root.has_deleted_children is True
assert root.is_leaf() is False
def test_models_documents_has_deleted_children():
"""
A document should have its has_deleted_children attribute set to True if one of its children
has been solf deleted no matter if numchild is 0 or not.
"""
root = factories.DocumentFactory()
child = factories.DocumentFactory(parent=root)
assert root.has_deleted_children is False
child.soft_delete()
root.refresh_from_db()
assert root.has_deleted_children is True