From 1fa084b6be40846e80ea3a24ad357a2d990ece2d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 26 Apr 2017 12:53:14 -0400 Subject: [PATCH 1/4] Fixes #1101: Fix AJAX scripting for device component selection forms --- netbox/ipam/forms.py | 2 +- netbox/project-static/js/forms.js | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 6aa5347da..bfbecc878 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -316,7 +316,7 @@ class IPAddressForm(BootstrapMixin, ReturnURLForm, CustomFieldForm): interface_rack = forms.ModelChoiceField( queryset=Rack.objects.all(), required=False, label='Rack', widget=APISelect( api_url='/api/dcim/racks/?site_id={{interface_site}}', display_field='display_name', - attrs={'filter-for': 'interface_device'} + attrs={'filter-for': 'interface_device', 'nullable': 'true'} ) ) interface_device = forms.ModelChoiceField( diff --git a/netbox/project-static/js/forms.js b/netbox/project-static/js/forms.js index e421f6283..b56be0921 100644 --- a/netbox/project-static/js/forms.js +++ b/netbox/project-static/js/forms.js @@ -88,20 +88,21 @@ $(document).ready(function() { // Determine the filter fields needed to make an API call var filter_regex = /\{\{([a-z_]+)\}\}/g; var match; + var rendered_url = api_url; while (match = filter_regex.exec(api_url)) { var filter_field = $('#id_' + match[1]); if (filter_field.val()) { - api_url = api_url.replace(match[0], filter_field.val()); - } else if ($(this).attr('nullable') == 'true') { - api_url = api_url.replace(match[0], '0'); + rendered_url = rendered_url.replace(match[0], filter_field.val()); + } else if (filter_field.attr('nullable') == 'true') { + rendered_url = rendered_url.replace(match[0], '0'); } } // If all URL variables have been replaced, make the API call - if (api_url.search('{{') < 0) { - console.log(child_name + ": Fetching " + api_url); + if (rendered_url.search('{{') < 0) { + console.log(child_name + ": Fetching " + rendered_url); $.ajax({ - url: api_url, + url: rendered_url, dataType: 'json', success: function (response, status) { $.each(response, function (index, choice) { From 480faa6461b628b3088efc9272aff62764466e4d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 26 Apr 2017 13:03:18 -0400 Subject: [PATCH 2/4] Removed deprecated IPAddressAssignForm --- netbox/ipam/forms.py | 59 -------------------------------------------- 1 file changed, 59 deletions(-) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index bfbecc878..a5b21e53b 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -429,65 +429,6 @@ class IPAddressBulkAddForm(BootstrapMixin, CustomFieldForm): fields = ['address_pattern', 'vrf', 'tenant', 'status', 'description'] -class IPAddressAssignForm(BootstrapMixin, forms.Form): - site = forms.ModelChoiceField( - queryset=Site.objects.all(), - label='Site', - required=False, - widget=forms.Select( - attrs={'filter-for': 'rack'} - ) - ) - rack = forms.ModelChoiceField( - queryset=Rack.objects.all(), - label='Rack', - required=False, - widget=APISelect( - api_url='/api/dcim/racks/?site_id={{site}}', - display_field='display_name', - attrs={'filter-for': 'device', 'nullable': 'true'} - ) - ) - device = forms.ModelChoiceField( - queryset=Device.objects.all(), - label='Device', - required=False, - widget=APISelect( - api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}', - display_field='display_name', - attrs={'filter-for': 'interface'} - ) - ) - livesearch = forms.CharField( - required=False, - label='Device', - widget=Livesearch( - query_key='q', - query_url='dcim-api:device_list', - field_to_update='device' - ) - ) - interface = forms.ModelChoiceField( - queryset=Interface.objects.all(), - label='Interface', - widget=APISelect( - api_url='/api/dcim/devices/{{device}}/interfaces/' - ) - ) - set_as_primary = forms.BooleanField( - label='Set as primary IP for device', - required=False - ) - - def __init__(self, *args, **kwargs): - - super(IPAddressAssignForm, self).__init__(*args, **kwargs) - - self.fields['rack'].choices = [] - self.fields['device'].choices = [] - self.fields['interface'].choices = [] - - class IPAddressFromCSVForm(forms.ModelForm): vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, to_field_name='rd', error_messages={'invalid_choice': 'VRF not found.'}) From f643f2c6017833e9996988f455d013938286ea5e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 26 Apr 2017 13:21:38 -0400 Subject: [PATCH 3/4] Fixes #1103: Correct handling of validation errors when creating IP addresses in bulk --- netbox/utilities/views.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 71e385a6e..1985df2ac 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -329,13 +329,21 @@ class BulkAddView(View): new_objs = [] try: with transaction.atomic(): + # Validate and save each object individually for value in pattern: 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 model_form.is_valid(): + obj = model_form.save() + new_objs.append(obj) + else: + for error in model_form.errors.as_data().values(): + form.add_error(None, error) + # Abort the creation of all objects if errors exist + if form.errors: + raise ValidationError("Validation of one or more model forms failed.") + except ValidationError: + pass if not form.errors: msg = u"Added {} {}".format(len(new_objs), model._meta.verbose_name_plural) From e7a6d1f5323986051fd37e15dea6c13628065521 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 26 Apr 2017 13:28:09 -0400 Subject: [PATCH 4/4] Fixes #1104: Fix VLAN assignment on prefix import --- netbox/ipam/forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index a5b21e53b..33f93d26b 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -236,7 +236,6 @@ class PrefixFromCSVForm(forms.ModelForm): self.add_error('vlan_vid', "Invalid global VLAN ID ({}).".format(vlan_vid)) except VLAN.MultipleObjectsReturned: self.add_error('vlan_vid', "Multiple VLANs found ({} - VID {})".format(site, vlan_vid)) - self.instance.vlan = vlan def save(self, *args, **kwargs):