Просмотр исходного кода

Fixes #14081: Fix cached counters on delete for parent-child items (#14131)

* 14081 fixed cached counters on delete for parent-child items

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Arthur Hanson 2 лет назад
Родитель
Сommit
b93735861d
1 измененных файлов с 15 добавлено и 5 удалено
  1. 15 5
      netbox/utilities/counters.py

+ 15 - 5
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,