Răsfoiți Sursa

14042 mptt cache count (#14048)

* 14042 fix cache count for mptt child delete

* 14042 add test

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Arthur Hanson 2 ani în urmă
părinte
comite
a24864bc6d
2 a modificat fișierele cu 32 adăugiri și 5 ștergeri
  1. 4 2
      netbox/utilities/counters.py
  2. 28 3
      netbox/utilities/tests/test_counters.py

+ 4 - 2
netbox/utilities/counters.py

@@ -62,7 +62,7 @@ def post_save_receiver(sender, instance, created, **kwargs):
             update_counter(parent_model, new_pk, counter_name, 1)
 
 
-def post_delete_receiver(sender, instance, **kwargs):
+def post_delete_receiver(sender, instance, origin, **kwargs):
     """
     Update counter fields on related objects when a TrackingModelMixin subclass is deleted.
     """
@@ -72,7 +72,9 @@ def post_delete_receiver(sender, instance, **kwargs):
 
         # Decrement the parent's counter by one
         if parent_pk is not None:
-            update_counter(parent_model, parent_pk, counter_name, -1)
+            # 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)
 
 
 #

+ 28 - 3
netbox/utilities/tests/test_counters.py

@@ -1,7 +1,11 @@
-from django.test import TestCase
+from django.contrib.contenttypes.models import ContentType
+from django.test import override_settings
+from django.urls import reverse
 
 from dcim.models import *
-from utilities.testing.utils import create_test_device
+from users.models import ObjectPermission
+from utilities.testing.base import TestCase
+from utilities.testing.utils import create_test_device, create_test_user
 
 
 class CountersTest(TestCase):
@@ -10,7 +14,6 @@ class CountersTest(TestCase):
     """
     @classmethod
     def setUpTestData(cls):
-
         # Create devices
         device1 = create_test_device('Device 1')
         device2 = create_test_device('Device 2')
@@ -79,3 +82,25 @@ class CountersTest(TestCase):
         device2.refresh_from_db()
         self.assertEqual(device1.interface_count, 1)
         self.assertEqual(device2.interface_count, 3)
+
+    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+    def test_mptt_child_delete(self):
+        device1, device2 = Device.objects.all()
+        inventory_item1 = InventoryItem.objects.create(device=device1, name='Inventory Item 1')
+        inventory_item2 = InventoryItem.objects.create(device=device1, name='Inventory Item 2', parent=inventory_item1)
+        device1.refresh_from_db()
+        self.assertEqual(device1.inventory_item_count, 2)
+
+        # Setup bulk_delete for the inventory items
+        self.add_permissions('dcim.delete_inventoryitem')
+        pk_list = device1.inventoryitems.values_list('pk', flat=True)
+        data = {
+            'pk': pk_list,
+            'confirm': True,
+            '_confirm': True,  # Form button
+        }
+
+        # Try POST with model-level permission
+        self.client.post(reverse("dcim:inventoryitem_bulk_delete"), data)
+        device1.refresh_from_db()
+        self.assertEqual(device1.inventory_item_count, 0)