diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index c9701f60b..6aa5347da 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -418,12 +418,15 @@ class IPAddressForm(BootstrapMixin, ReturnURLForm, CustomFieldForm): self.fields['nat_inside'].choices = [] -class IPAddressBulkAddForm(BootstrapMixin, forms.Form): - address = ExpandableIPAddressField() +class IPAddressBulkAddForm(BootstrapMixin, CustomFieldForm): + address_pattern = ExpandableIPAddressField(label='Address Pattern') vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF', empty_label='Global') - tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False) - status = forms.ChoiceField(choices=IPADDRESS_STATUS_CHOICES) - description = forms.CharField(max_length=100, required=False) + + pattern_map = ('address_pattern', 'address') + + class Meta: + model = IPAddress + fields = ['address_pattern', 'vrf', 'tenant', 'status', 'description'] class IPAddressAssignForm(BootstrapMixin, forms.Form): diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 0c53b8bc9..0754e0fa7 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -588,7 +588,7 @@ class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView): class IPAddressBulkAddView(PermissionRequiredMixin, BulkAddView): permission_required = 'ipam.add_ipaddress' form = forms.IPAddressBulkAddForm - model = IPAddress + model_form = forms.IPAddressForm template_name = 'ipam/ipaddress_bulk_add.html' default_return_url = 'ipam:ipaddress_list' diff --git a/netbox/templates/ipam/ipaddress_bulk_add.html b/netbox/templates/ipam/ipaddress_bulk_add.html index 1599ee900..d53f73bd5 100644 --- a/netbox/templates/ipam/ipaddress_bulk_add.html +++ b/netbox/templates/ipam/ipaddress_bulk_add.html @@ -10,13 +10,21 @@ {% block form %}
-
IP Address
+
IP Addresses
- {% render_field form.address %} + {% render_field form.address_pattern %} {% render_field form.vrf %} {% render_field form.tenant %} {% render_field form.status %} {% render_field form.description %}
+ {% if form.custom_fields %} +
+
Custom Fields
+
+ {% render_custom_fields form %} +
+
+ {% endif %} {% endblock %} diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 0f867e16b..71e385a6e 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -296,12 +296,12 @@ class BulkAddView(View): Create new objects in bulk. form: Form class - model: The model of the objects being created + model_form: The ModelForm used to create individual objects template_name: The name of the template default_return_url: Name of the URL to which the user is redirected after creating the objects """ form = None - model = None + model_form = None template_name = None default_return_url = 'home' @@ -310,47 +310,44 @@ class BulkAddView(View): form = self.form() return render(request, self.template_name, { - 'obj_type': self.model._meta.verbose_name, + 'obj_type': self.model_form._meta.model._meta.verbose_name, 'form': form, 'return_url': reverse(self.default_return_url), }) def post(self, request): + model = self.model_form._meta.model form = self.form(request.POST) if form.is_valid(): - # The first field will be used as the pattern - field_names = list(form.fields.keys()) - pattern_field = field_names[0] + # Read the pattern field and target from the form's pattern_map + pattern_field, pattern_target = form.pattern_map pattern = form.cleaned_data[pattern_field] - - # All other fields will be copied as object attributes - kwargs = {k: form.cleaned_data[k] for k in field_names[1:]} + model_form_data = form.cleaned_data new_objs = [] try: with transaction.atomic(): for value in pattern: - obj = self.model(**kwargs) - setattr(obj, pattern_field, value) - obj.full_clean() - obj.save() + model_form_data[pattern_target] = value + model_form = self.model_form(model_form_data) + obj = model_form.save() new_objs.append(obj) except ValidationError as e: form.add_error(None, e) if not form.errors: - msg = u"Added {} {}".format(len(new_objs), self.model._meta.verbose_name_plural) + msg = u"Added {} {}".format(len(new_objs), model._meta.verbose_name_plural) messages.success(request, msg) - UserAction.objects.log_bulk_create(request.user, ContentType.objects.get_for_model(self.model), msg) + UserAction.objects.log_bulk_create(request.user, ContentType.objects.get_for_model(model), msg) if '_addanother' in request.POST: return redirect(request.path) return redirect(self.default_return_url) return render(request, self.template_name, { 'form': form, - 'obj_type': self.model._meta.verbose_name, + 'obj_type': model._meta.verbose_name, 'return_url': reverse(self.default_return_url), })