Fixes #1166: Re-implemented bulk IP address creation

This commit is contained in:
Jeremy Stretch 2017-05-12 12:00:26 -04:00
parent 008ed34553
commit a870a3b918
4 changed files with 59 additions and 42 deletions

View File

@ -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):

View File

@ -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'

View File

@ -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 %}

View File

@ -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),
}) })