Skip to content

Infinite recursive loop with SafeDeleteQueryset #233

@kursat-ozer

Description

@kursat-ozer

Hi,

I am having an issue similar to 117

I am using django-treebeard for hierarchical data in DB and deleting an item with descendants should also delete all related descendants. I also want these items to be safely deleted so policy is SOFT_DELETE_CASCADE.

I think I am using the treebeard and safedelete packages in the right way but if you see anything wrong with it please do tell:
Note: The models are slightly different but this is how it is basically.

class FolderQuerySet(MP_NodeQuerySet, SafeDeleteQueryset):
    pass

class Folder(MP_Node, SafeDeleteModel):
    ...
    objects = SafeDeleteManager.from_queryset(FolderQuerySet)()
    all_objects = SafeDeleteAllManager.from_queryset(FolderQuerySet)()
    deleted_objects = SafeDeleteDeletedManager.from_queryset(FolderQuerySet)()

When I try to delete a folder,

  • it first finds all descendants
  • then should safe delete all items and cascade.
    But in the SafeDeleteQueryset on line 66 it calls _, delete_response = obj.delete(force_policy=force_policy) and this causes the mp_node delete call and starts infinite recursion.
    Updating the line with _, delete_response = obj._delete(force_policy=force_policy) should fix this, right?
class SafeDeleteQueryset(query.QuerySet):
    ...

    def delete(self, force_policy: Optional[int] = None) -> Tuple[int, Dict[str, int]]:
        """Overrides bulk delete behaviour.

        .. note::
            The current implementation loses performance on bulk deletes in order
            to safely delete objects according to the deletion policies set.

        .. seealso::
            :py:func:`safedelete.models.SafeDeleteModel.delete`
        """
        assert self.query.can_filter(), "Cannot use 'limit' or 'offset' with delete."

        if force_policy == NO_DELETE:
            return (0, {})
        elif force_policy == HARD_DELETE:
            return self.hard_delete_policy_action()
        else:
            deleted_counter: Counter = Counter()
            # TODO: Replace this by bulk update if we can
            for obj in self.all():
                _, delete_response = obj.delete(force_policy=force_policy)
                deleted_counter.update(delete_response)
            self._result_cache = None
            return sum(deleted_counter.values()), dict(deleted_counter)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions