From 84670af18b388594a28117be1d55552387327e4a Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 26 Mar 2026 09:56:21 -0700 Subject: [PATCH] #20162 allow background job when adding components to devices in bulk --- netbox/dcim/forms/bulk_create.py | 3 ++- netbox/netbox/views/generic/bulk_views.py | 16 ++++++++++++++++ netbox/templates/generic/bulk_add_component.html | 11 ++++++++++- netbox/virtualization/forms/bulk_create.py | 3 ++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/forms/bulk_create.py b/netbox/dcim/forms/bulk_create.py index a13b2bf38..c6ac6271e 100644 --- a/netbox/dcim/forms/bulk_create.py +++ b/netbox/dcim/forms/bulk_create.py @@ -6,6 +6,7 @@ from extras.models import Tag from netbox.forms.mixins import CustomFieldsMixin from utilities.forms import form_from_model from utilities.forms.fields import DynamicModelMultipleChoiceField, ExpandableNameField +from utilities.forms.mixins import BackgroundJobMixin from .object_create import ComponentCreateForm @@ -27,7 +28,7 @@ __all__ = ( # Device components # -class DeviceBulkAddComponentForm(CustomFieldsMixin, ComponentCreateForm): +class DeviceBulkAddComponentForm(BackgroundJobMixin, CustomFieldsMixin, ComponentCreateForm): pk = forms.ModelMultipleChoiceField( queryset=Device.objects.all(), widget=forms.MultipleHiddenInput() diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 25756e212..243036091 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -1137,8 +1137,18 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): if form.is_valid(): logger.debug("Form validation was successful") + # If indicated, defer this request to a background job & redirect the user + if form.cleaned_data.get('background_job'): + job_name = _('Bulk add {count} {object_type}').format( + count=len(form.cleaned_data['pk']), + object_type=model_name, + ) + if process_request_as_job(self.__class__, request, name=job_name): + return redirect(self.get_return_url(request)) + new_components = [] data = deepcopy(form.cleaned_data) + data.pop('background_job', None) replication_data = { field: data.pop(field) for field in form.replication_fields } @@ -1189,6 +1199,12 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): parent_model_name ) logger.info(msg) + + # Handle background job + if is_background_request(request): + request.job.logger.info(msg) + return None + messages.success(request, msg) return redirect(self.get_return_url(request)) diff --git a/netbox/templates/generic/bulk_add_component.html b/netbox/templates/generic/bulk_add_component.html index 72502f441..078a45930 100644 --- a/netbox/templates/generic/bulk_add_component.html +++ b/netbox/templates/generic/bulk_add_component.html @@ -58,10 +58,19 @@ Context:

{{ model_name|title }} {% trans "to Add" %}

{% for field in form.visible_fields %} - {% render_field field %} + {% if form.meta_fields and field.name in form.meta_fields %} + {% else %} + {% render_field field %} + {% endif %} {% endfor %}
+ {# Meta fields #} + {% if form.background_job %} +
+ {% render_field form.background_job %} +
+ {% endif %}
{% trans "Cancel" %} diff --git a/netbox/virtualization/forms/bulk_create.py b/netbox/virtualization/forms/bulk_create.py index 078fb2f43..a1d7c5e54 100644 --- a/netbox/virtualization/forms/bulk_create.py +++ b/netbox/virtualization/forms/bulk_create.py @@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _ from utilities.forms import form_from_model from utilities.forms.fields import ExpandableNameField +from utilities.forms.mixins import BackgroundJobMixin from virtualization.models import VirtualDisk, VirtualMachine, VMInterface __all__ = ( @@ -11,7 +12,7 @@ __all__ = ( ) -class VirtualMachineBulkAddComponentForm(forms.Form): +class VirtualMachineBulkAddComponentForm(BackgroundJobMixin, forms.Form): pk = forms.ModelMultipleChoiceField( queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput()