diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 4ed80d6c8..32cf03253 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1,7 +1,10 @@ +import logging +from copy import deepcopy from collections import OrderedDict from django.contrib import messages from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist from django.core.paginator import EmptyPage, PageNotAnInteger from django.db import transaction from django.db.models import F, Prefetch @@ -1910,6 +1913,30 @@ class InterfaceCreateView(generic.ComponentCreateView): model_form = forms.InterfaceForm template_name = 'dcim/device_component_add.html' + def post(self, request): + """ + Override inherited post() method to handle request to assign newly created + interface objects (first object) to an IP Address object. + """ + logger = logging.getLogger('netbox.dcim.views.InterfaceCreateView') + form = self.form(request.POST, initial=request.GET) + new_objs = self.validate_form(request, form) + + if form.is_valid() and not form.errors: + if '_addanother' in request.POST: + return redirect(request.get_full_path()) + elif new_objs is not None and '_assignip' in request.POST and len(new_objs) >= 1 and request.user.has_perm('ipam.add_ipaddress'): + first_obj = new_objs[0].pk + return redirect(f'/ipam/ip-addresses/add/?interface={first_obj}&return_url={self.get_return_url(request)}') + else: + return redirect(self.get_return_url(request)) + + return render(request, self.template_name, { + 'component_type': self.queryset.model._meta.verbose_name, + 'form': form, + 'return_url': self.get_return_url(request), + }) + class InterfaceEditView(generic.ObjectEditView): queryset = Interface.objects.all() diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index 8f713fa63..a92e8b8af 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -1105,25 +1105,47 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View def post(self, request): logger = logging.getLogger('netbox.views.ComponentCreateView') form = self.form(request.POST, initial=request.GET) + self.validate_form(request, form) + if form.is_valid() and not form.errors: + if '_addanother' in request.POST: + return redirect(request.get_full_path()) + else: + return redirect(self.get_return_url(request)) + + return render(request, self.template_name, { + 'component_type': self.queryset.model._meta.verbose_name, + 'form': form, + 'return_url': self.get_return_url(request), + }) + + def validate_form(self, request, form): + """ + Validate form values and set errors on the form object as they are detected. If + no errors are found, signal success messages. + """ + + logger = logging.getLogger('netbox.views.ComponentCreateView') if form.is_valid(): - new_components = [] data = deepcopy(request.POST) - names = form.cleaned_data['name_pattern'] labels = form.cleaned_data.get('label_pattern') + for i, name in enumerate(names): label = labels[i] if labels else None # Initialize the individual component form data['name'] = name data['label'] = label + if hasattr(form, 'get_iterative_data'): data.update(form.get_iterative_data(i)) + component_form = self.model_form(data) if component_form.is_valid(): new_components.append(component_form) + else: for field, errors in component_form.errors.as_data().items(): # Assign errors on the child form's name/label field to name_pattern/label_pattern on the parent form @@ -1135,11 +1157,8 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View form.add_error(field, '{}: {}'.format(name, ', '.join(e))) if not form.errors: - try: - with transaction.atomic(): - # Create the new components new_objs = [] for component_form in new_components: @@ -1150,24 +1169,17 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View if self.queryset.filter(pk__in=[obj.pk for obj in new_objs]).count() != len(new_objs): raise ObjectDoesNotExist - messages.success(request, "Added {} {}".format( - len(new_components), self.queryset.model._meta.verbose_name_plural - )) - if '_addanother' in request.POST: - return redirect(request.get_full_path()) - else: - return redirect(self.get_return_url(request)) + messages.success(request, "Added {} {}".format( + len(new_components), self.queryset.model._meta.verbose_name_plural + )) + # Return the newly created objects so overridden post methods can use the data as needed. + return new_objs except ObjectDoesNotExist: msg = "Component creation failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) - - return render(request, self.template_name, { - 'component_type': self.queryset.model._meta.verbose_name, - 'form': form, - 'return_url': self.get_return_url(request), - }) + return None class BulkComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): diff --git a/netbox/templates/dcim/device_component_add.html b/netbox/templates/dcim/device_component_add.html index 40f5bfa63..e8b82f2c0 100644 --- a/netbox/templates/dcim/device_component_add.html +++ b/netbox/templates/dcim/device_component_add.html @@ -20,6 +20,11 @@