diff --git a/netbox/utilities/counters.py b/netbox/utilities/counters.py index 0ee2606db7..589dacbdbc 100644 --- a/netbox/utilities/counters.py +++ b/netbox/utilities/counters.py @@ -1,6 +1,6 @@ from django.apps import apps from django.db.models import F, Count, OuterRef, Subquery -from django.db.models.signals import post_delete, post_save +from django.db.models.signals import post_delete, post_save, pre_delete from netbox.registry import registry from .fields import CounterCacheField @@ -62,6 +62,12 @@ def post_save_receiver(sender, instance, created, **kwargs): update_counter(parent_model, new_pk, counter_name, 1) +def pre_delete_receiver(sender, instance, origin, **kwargs): + model = instance._meta.model + if not model.objects.filter(pk=instance.pk).exists(): + instance._previously_removed = True + + def post_delete_receiver(sender, instance, origin, **kwargs): """ Update counter fields on related objects when a TrackingModelMixin subclass is deleted. @@ -71,10 +77,8 @@ def post_delete_receiver(sender, instance, origin, **kwargs): parent_pk = getattr(instance, field_name, None) # Decrement the parent's counter by one - if parent_pk is not None: - # MPTT sends two delete signals for child elements so guard against multiple decrements - if not origin or origin == instance: - update_counter(parent_model, parent_pk, counter_name, -1) + if parent_pk is not None and not hasattr(instance, "_previously_removed"): + update_counter(parent_model, parent_pk, counter_name, -1) # @@ -106,6 +110,12 @@ def connect_counters(*models): weak=False, dispatch_uid=f'{model._meta.label}.{field.name}' ) + pre_delete.connect( + pre_delete_receiver, + sender=to_model, + weak=False, + dispatch_uid=f'{model._meta.label}.{field.name}' + ) post_delete.connect( post_delete_receiver, sender=to_model,