mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
Fixes #1166: Re-implemented bulk IP address creation
This commit is contained in:
parent
008ed34553
commit
a870a3b918
@ -471,15 +471,19 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm)
|
|||||||
return ipaddress
|
return ipaddress
|
||||||
|
|
||||||
|
|
||||||
class IPAddressBulkAddForm(BootstrapMixin, CustomFieldForm):
|
class IPAddressPatternForm(BootstrapMixin, forms.Form):
|
||||||
address_pattern = ExpandableIPAddressField(label='Address Pattern')
|
pattern = ExpandableIPAddressField(label='Address pattern')
|
||||||
vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, label='VRF', empty_label='Global')
|
|
||||||
|
|
||||||
pattern_map = ('address_pattern', 'address')
|
|
||||||
|
class IPAddressBulkAddForm(BootstrapMixin, CustomFieldForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
fields = ['address_pattern', 'vrf', 'tenant', 'status', 'description']
|
fields = ['address', 'status', 'vrf', 'tenant', 'description']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(IPAddressBulkAddForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['vrf'].empty_label = 'Global'
|
||||||
|
|
||||||
|
|
||||||
class IPAddressFromCSVForm(forms.ModelForm):
|
class IPAddressFromCSVForm(forms.ModelForm):
|
||||||
|
@ -584,8 +584,9 @@ class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
|||||||
|
|
||||||
class IPAddressBulkAddView(PermissionRequiredMixin, BulkAddView):
|
class IPAddressBulkAddView(PermissionRequiredMixin, BulkAddView):
|
||||||
permission_required = 'ipam.add_ipaddress'
|
permission_required = 'ipam.add_ipaddress'
|
||||||
form = forms.IPAddressBulkAddForm
|
pattern_form = forms.IPAddressPatternForm
|
||||||
model_form = forms.IPAddressForm
|
model_form = forms.IPAddressBulkAddForm
|
||||||
|
pattern_target = 'address'
|
||||||
template_name = 'ipam/ipaddress_bulk_add.html'
|
template_name = 'ipam/ipaddress_bulk_add.html'
|
||||||
default_return_url = 'ipam:ipaddress_list'
|
default_return_url = 'ipam:ipaddress_list'
|
||||||
|
|
||||||
|
@ -12,18 +12,18 @@
|
|||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><strong>IP Addresses</strong></div>
|
<div class="panel-heading"><strong>IP Addresses</strong></div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% render_field form.address_pattern %}
|
{% render_field pattern_form.pattern %}
|
||||||
{% render_field form.vrf %}
|
{% render_field model_form.status %}
|
||||||
{% render_field form.tenant %}
|
{% render_field model_form.vrf %}
|
||||||
{% render_field form.status %}
|
{% render_field model_form.tenant %}
|
||||||
{% render_field form.description %}
|
{% render_field model_form.description %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if form.custom_fields %}
|
{% if model_form.custom_fields %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><strong>Custom Fields</strong></div>
|
<div class="panel-heading"><strong>Custom Fields</strong></div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% render_custom_fields form %}
|
{% render_custom_fields model_form %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -290,66 +290,78 @@ class BulkAddView(View):
|
|||||||
"""
|
"""
|
||||||
Create new objects in bulk.
|
Create new objects in bulk.
|
||||||
|
|
||||||
form: Form class
|
pattern_form: Form class which provides the `pattern` field
|
||||||
model_form: The ModelForm used to create individual objects
|
model_form: The ModelForm used to create individual objects
|
||||||
template_name: The name of the template
|
template_name: The name of the template
|
||||||
default_return_url: Name of the URL to which the user is redirected after creating the objects
|
default_return_url: Name of the URL to which the user is redirected after creating the objects
|
||||||
"""
|
"""
|
||||||
form = None
|
pattern_form = None
|
||||||
model_form = None
|
model_form = None
|
||||||
|
pattern_target = ''
|
||||||
template_name = None
|
template_name = None
|
||||||
default_return_url = 'home'
|
default_return_url = 'home'
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
||||||
form = self.form()
|
pattern_form = self.pattern_form()
|
||||||
|
model_form = self.model_form()
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
'obj_type': self.model_form._meta.model._meta.verbose_name,
|
'obj_type': self.model_form._meta.model._meta.verbose_name,
|
||||||
'form': form,
|
'pattern_form': pattern_form,
|
||||||
|
'model_form': model_form,
|
||||||
'return_url': reverse(self.default_return_url),
|
'return_url': reverse(self.default_return_url),
|
||||||
})
|
})
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
|
|
||||||
model = self.model_form._meta.model
|
model = self.model_form._meta.model
|
||||||
form = self.form(request.POST)
|
pattern_form = self.pattern_form(request.POST)
|
||||||
if form.is_valid():
|
model_form = self.model_form(request.POST)
|
||||||
|
|
||||||
# Read the pattern field and target from the form's pattern_map
|
if pattern_form.is_valid():
|
||||||
pattern_field, pattern_target = form.pattern_map
|
|
||||||
pattern = form.cleaned_data[pattern_field]
|
|
||||||
model_form_data = form.cleaned_data
|
|
||||||
|
|
||||||
|
pattern = pattern_form.cleaned_data['pattern']
|
||||||
new_objs = []
|
new_objs = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
# Validate and save each object individually
|
|
||||||
|
# Create objects from the expanded. Abort the transaction on the first validation error.
|
||||||
for value in pattern:
|
for value in pattern:
|
||||||
model_form_data[pattern_target] = value
|
|
||||||
model_form = self.model_form(model_form_data)
|
# Reinstantiate the model form each time to avoid overwriting the same instance. Use a mutable
|
||||||
|
# copy of the POST QueryDict so that we can update the target field value.
|
||||||
|
model_form = self.model_form(request.POST.copy())
|
||||||
|
model_form.data[self.pattern_target] = value
|
||||||
|
|
||||||
|
# Validate each new object independently.
|
||||||
if model_form.is_valid():
|
if model_form.is_valid():
|
||||||
obj = model_form.save()
|
obj = model_form.save()
|
||||||
new_objs.append(obj)
|
new_objs.append(obj)
|
||||||
else:
|
else:
|
||||||
for error in model_form.errors.as_data().values():
|
# Copy any errors on the pattern target field to the pattern form.
|
||||||
form.add_error(None, error)
|
errors = model_form.errors.as_data()
|
||||||
# Abort the creation of all objects if errors exist
|
if errors.get(self.pattern_target):
|
||||||
if form.errors:
|
pattern_form.add_error('pattern', errors[self.pattern_target])
|
||||||
raise ValidationError("Validation of one or more model forms failed.")
|
# Raise an IntegrityError to break the for loop and abort the transaction.
|
||||||
except ValidationError:
|
raise IntegrityError()
|
||||||
|
|
||||||
|
# If we make it to this point, validation has succeeded on all new objects.
|
||||||
|
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(model), msg)
|
||||||
|
|
||||||
|
if '_addanother' in request.POST:
|
||||||
|
return redirect(request.path)
|
||||||
|
return redirect(self.default_return_url)
|
||||||
|
|
||||||
|
except IntegrityError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not form.errors:
|
|
||||||
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(model), msg)
|
|
||||||
if '_addanother' in request.POST:
|
|
||||||
return redirect(request.path)
|
|
||||||
return redirect(self.default_return_url)
|
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
'form': form,
|
'pattern_form': pattern_form,
|
||||||
|
'model_form': model_form,
|
||||||
'obj_type': model._meta.verbose_name,
|
'obj_type': model._meta.verbose_name,
|
||||||
'return_url': reverse(self.default_return_url),
|
'return_url': reverse(self.default_return_url),
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user