diff --git a/netbox/extras/tests/test_scripts_deletion.py b/netbox/extras/tests/test_scripts_deletion.py index 1d5c92581..f6d9bad48 100644 --- a/netbox/extras/tests/test_scripts_deletion.py +++ b/netbox/extras/tests/test_scripts_deletion.py @@ -7,7 +7,7 @@ from django.db.models import QuerySet from django.test import TestCase, override_settings from django.urls import reverse -from core.choices import ManagedFileRootPathChoices +from core.choices import JobStatusChoices, ManagedFileRootPathChoices from core.models import DataSource, Job from extras.models import Script, ScriptModule from extras.validators import CustomValidator @@ -43,7 +43,7 @@ class ScriptDeletionTestCase(TestCase): object_type=object_type, object_id=obj.pk, name='testjob', - status='completed', + status=JobStatusChoices.STATUS_COMPLETED, job_id=uuid.uuid4(), data={'output': 'x' * 50}, ) @@ -164,7 +164,7 @@ class ConfirmCollectorTestCase(TestCase): script = Script.objects.create(module=module, name=f'S{uuid.uuid4().hex[:8]}') ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) Job.objects.bulk_create([ - Job(object_type=ct, object_id=script.pk, name='j', status='completed', + Job(object_type=ct, object_id=script.pk, name='j', status=JobStatusChoices.STATUS_COMPLETED, job_id=uuid.uuid4(), data={'output': 'x' * 50}) for _ in range(count) ]) @@ -226,7 +226,10 @@ class ObjectDeleteViewCountsTestCase(ViewTestCase): script = Script.objects.create(module=module, name=f'S{uuid.uuid4().hex[:8]}') ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) Job.objects.bulk_create([ - Job(object_type=ct, object_id=script.pk, name='j', status='completed', job_id=uuid.uuid4()) + Job( + object_type=ct, object_id=script.pk, name='j', + status=JobStatusChoices.STATUS_COMPLETED, job_id=uuid.uuid4(), + ) for _ in range(50) ]) @@ -249,7 +252,10 @@ class ObjectDeleteViewCountsTestCase(ViewTestCase): script = Script.objects.create(module=module, name=f'S{uuid.uuid4().hex[:8]}') ct = ContentType.objects.get_for_model(Script, for_concrete_model=False) Job.objects.bulk_create([ - Job(object_type=ct, object_id=script.pk, name='j', status='completed', job_id=uuid.uuid4()) + Job( + object_type=ct, object_id=script.pk, name='j', + status=JobStatusChoices.STATUS_COMPLETED, job_id=uuid.uuid4(), + ) for _ in range(50) ]) diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index f608da50c..4f1d5b684 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -464,7 +464,10 @@ def batch_delete_jobs(job_queryset): # Re-slice the queryset each iteration: it re-queries after each batch delete, so the # remaining set shrinks and the loop terminates (do not hoist this into a cursor). while pks := list(job_pks[:JOB_DELETE_BATCH_SIZE]): - Job.objects.filter(pk__in=pks).delete() + # only('pk'): the batch still can't fast-delete (a global pre_delete receiver forces + # per-instance signals), so each Job in the batch is instantiated. Loading just the PK + # avoids pulling the large data/log_entries payloads into those instances. + Job.objects.filter(pk__in=pks).only('pk').delete() class JobsMixin(models.Model): diff --git a/netbox/templates/htmx/delete_form.html b/netbox/templates/htmx/delete_form.html index 8f24816a4..c07e83d0a 100644 --- a/netbox/templates/htmx/delete_form.html +++ b/netbox/templates/htmx/delete_form.html @@ -30,7 +30,7 @@ {# High-cardinality relations (e.g. jobs) are summarized by count and are not #} {# expandable, since their instances are intentionally not loaded (see #22812). #} {% if instances.count_only %} -