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

Fixes #22812: Don't show a spurious "0 jobs" row for jobless objects

Django's Collector calls into the jobs GenericRelation branch unconditionally, so
ConfirmCollector recorded a zero count for objects with no jobs. _get_dependent_objects
then added a CountOnly(0), and the delete-confirmation page rendered "The following
objects will be deleted as a result of this action." plus a "0 jobs" row for every
jobless JobsMixin object. Only record a count when there are actually jobs.
Jason Novinger 2 недель назад
Родитель
Сommit
be69c55a99
2 измененных файлов с 31 добавлено и 1 удалено
  1. 25 0
      netbox/extras/tests/test_scripts_deletion.py
  2. 6 1
      netbox/netbox/models/deletion.py

+ 25 - 0
netbox/extras/tests/test_scripts_deletion.py

@@ -199,6 +199,16 @@ class ConfirmCollectorTestCase(TestCase):
         self.assertEqual(list(wrapper), [])
         self.assertEqual(list(wrapper), [])
         self.assertTrue(wrapper.count_only)
         self.assertTrue(wrapper.count_only)
 
 
+    def test_confirm_collector_omits_jobs_when_none(self):
+        # A jobless object must not record a zero count, or the confirmation page would show a
+        # spurious "0 jobs" row (#22812 regression).
+        datasource = DataSource.objects.create(name='DS', type='local', source_url='/tmp/test')
+
+        collector = ConfirmCollector(using=router.db_for_write(DataSource))
+        collector.collect([datasource])
+
+        self.assertNotIn(Job, collector.generic_relation_counts)
+
 
 
 class ObjectDeleteViewCountsTestCase(ViewTestCase):
 class ObjectDeleteViewCountsTestCase(ViewTestCase):
     """
     """
@@ -258,3 +268,18 @@ class ObjectDeleteViewCountsTestCase(ViewTestCase):
         self.assertIsInstance(dependent_objects[Job], CountOnly)
         self.assertIsInstance(dependent_objects[Job], CountOnly)
         self.assertEqual(len(dependent_objects[Job]), 50)
         self.assertEqual(len(dependent_objects[Job]), 50)
         self.assertTrue(dependent_objects[Job].count_only)
         self.assertTrue(dependent_objects[Job].count_only)
+
+    def test_get_dependent_objects_omits_jobs_when_none(self):
+        from netbox.views.generic.object_views import ObjectDeleteView
+
+        # A module with no jobs must not produce a CountOnly(0) entry (#22812 regression).
+        module = ScriptModule.objects.create(
+            file_root=ManagedFileRootPathChoices.SCRIPTS,
+            file_path=f'test_{uuid.uuid4().hex[:8]}.py',
+        )
+
+        view = ObjectDeleteView()
+        view.queryset = ScriptModule.objects.all()
+        dependent_objects = view._get_dependent_objects(module)
+
+        self.assertNotIn(Job, dependent_objects)

+ 6 - 1
netbox/netbox/models/deletion.py

@@ -59,7 +59,12 @@ class ConfirmCollector(Collector):
         from core.models import Job
         from core.models import Job
 
 
         if source is not None and getattr(objs, 'model', None) is Job:
         if source is not None and getattr(objs, 'model', None) is Job:
-            self.generic_relation_counts[Job] = self.generic_relation_counts.get(Job, 0) + objs.count()
+            # Django calls this branch for the jobs relation even when there are none; only record
+            # a count when there are actually jobs, so jobless objects don't get a spurious
+            # "0 jobs" row on the delete-confirmation page.
+            count = objs.count()
+            if count:
+                self.generic_relation_counts[Job] = self.generic_relation_counts.get(Job, 0) + count
             return None
             return None
         return super().collect(objs, source=source, *args, **kwargs)
         return super().collect(objs, source=source, *args, **kwargs)