Parcourir la source

Rewrote all DeviceType component template deletion views to utilize BulkDeleteView()

Jeremy Stretch il y a 8 ans
Parent
commit
d47bf4ab6b

+ 8 - 0
netbox/dcim/forms.py

@@ -251,6 +251,10 @@ class ConsolePortTemplateForm(forms.ModelForm, BootstrapMixin):
         fields = ['name_pattern']
 
 
+class ConsolePortTemplateBulkDeleteForm(ConfirmationForm):
+    pk = forms.ModelMultipleChoiceField(queryset=ConsolePortTemplate.objects.all(), widget=forms.MultipleHiddenInput)
+
+
 class ConsoleServerPortTemplateForm(forms.ModelForm, BootstrapMixin):
     name_pattern = ExpandableNameField(label='Name')
 
@@ -259,6 +263,10 @@ class ConsoleServerPortTemplateForm(forms.ModelForm, BootstrapMixin):
         fields = ['name_pattern']
 
 
+class ConsoleServerPortTemplateBulkDeleteForm(ConfirmationForm):
+    pk = forms.ModelMultipleChoiceField(queryset=ConsoleServerPortTemplate.objects.all(), widget=forms.MultipleHiddenInput)
+
+
 class PowerPortTemplateForm(forms.ModelForm, BootstrapMixin):
     name_pattern = ExpandableNameField(label='Name')
 

+ 23 - 25
netbox/dcim/urls.py

@@ -50,31 +50,29 @@ urlpatterns = [
     url(r'^device-types/(?P<pk>\d+)/edit/$', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
     url(r'^device-types/(?P<pk>\d+)/delete/$', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
 
-    # Component templates
-    url(r'^device-types/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortTemplateAddView.as_view(),
-        name='devicetype_add_consoleport'),
-    url(r'^device-types/(?P<pk>\d+)/console-ports/delete/$', views.component_template_delete,
-        {'model': ConsolePortTemplate}, name='devicetype_delete_consoleport'),
-    url(r'^device-types/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortTemplateAddView.as_view(),
-        name='devicetype_add_consoleserverport'),
-    url(r'^device-types/(?P<pk>\d+)/console-server-ports/delete/$', views.component_template_delete,
-        {'model': ConsoleServerPortTemplate}, name='devicetype_delete_consoleserverport'),
-    url(r'^device-types/(?P<pk>\d+)/power-ports/add/$', views.PowerPortTemplateAddView.as_view(),
-        name='devicetype_add_powerport'),
-    url(r'^device-types/(?P<pk>\d+)/power-ports/delete/$', views.component_template_delete,
-        {'model': PowerPortTemplate}, name='devicetype_delete_powerport'),
-    url(r'^device-types/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletTemplateAddView.as_view(),
-        name='devicetype_add_poweroutlet'),
-    url(r'^device-types/(?P<pk>\d+)/power-outlets/delete/$', views.component_template_delete,
-        {'model': PowerOutletTemplate}, name='devicetype_delete_poweroutlet'),
-    url(r'^device-types/(?P<pk>\d+)/interfaces/add/$', views.InterfaceTemplateAddView.as_view(),
-        name='devicetype_add_interface'),
-    url(r'^device-types/(?P<pk>\d+)/interfaces/delete/$', views.component_template_delete,
-        {'model': InterfaceTemplate}, name='devicetype_delete_interface'),
-    url(r'^device-types/(?P<pk>\d+)/device-bays/add/$', views.DeviceBayTemplateAddView.as_view(),
-        name='devicetype_add_devicebay'),
-    url(r'^device-types/(?P<pk>\d+)/device-bays/delete/$', views.component_template_delete,
-        {'model': DeviceBayTemplate}, name='devicetype_delete_devicebay'),
+    # Console port templates
+    url(r'^device-types/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortTemplateAddView.as_view(), name='devicetype_add_consoleport'),
+    url(r'^device-types/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleport'),
+
+    # Console server port templates
+    url(r'^device-types/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortTemplateAddView.as_view(), name='devicetype_add_consoleserverport'),
+    url(r'^device-types/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleserverport'),
+
+    # Power port templates
+    url(r'^device-types/(?P<pk>\d+)/power-ports/add/$', views.PowerPortTemplateAddView.as_view(), name='devicetype_add_powerport'),
+    url(r'^device-types/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_powerport'),
+
+    # Power outlet templates
+    url(r'^device-types/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletTemplateAddView.as_view(), name='devicetype_add_poweroutlet'),
+    url(r'^device-types/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletTemplateBulkDeleteView.as_view(), name='devicetype_delete_poweroutlet'),
+
+    # Interface templates
+    url(r'^device-types/(?P<pk>\d+)/interfaces/add/$', views.InterfaceTemplateAddView.as_view(), name='devicetype_add_interface'),
+    url(r'^device-types/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceTemplateBulkDeleteView.as_view(), name='devicetype_delete_interface'),
+
+    # Device bay templates
+    url(r'^device-types/(?P<pk>\d+)/device-bays/add/$', views.DeviceBayTemplateAddView.as_view(), name='devicetype_add_devicebay'),
+    url(r'^device-types/(?P<pk>\d+)/device-bays/delete/$', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicetype_delete_devicebay'),
 
     # Device roles
     url(r'^device-roles/$', views.DeviceRoleListView.as_view(), name='devicerole_list'),

+ 35 - 40
netbox/dcim/views.py

@@ -7,8 +7,7 @@ from django.contrib.auth.decorators import permission_required
 from django.contrib.auth.mixins import PermissionRequiredMixin
 from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
-from django.db.models import Count, ProtectedError, Sum
-from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
+from django.db.models import Count, Sum
 from django.http import HttpResponseRedirect
 from django.shortcuts import get_object_or_404, redirect, render
 from django.utils.http import urlencode
@@ -17,7 +16,6 @@ from django.views.generic import View
 from ipam.models import Prefix, IPAddress, VLAN
 from circuits.models import Circuit
 from extras.models import TopologyMap
-from utilities.error_handlers import handle_protectederror
 from utilities.forms import ConfirmationForm
 from utilities.views import (
     BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
@@ -396,68 +394,65 @@ class ConsolePortTemplateAddView(ComponentTemplateCreateView):
     form = forms.ConsolePortTemplateForm
 
 
+class ConsolePortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
+    permission_required = 'dcim.delete_consoleporttemplate'
+    cls = ConsolePortTemplate
+    parent_cls = DeviceType
+
+
 class ConsoleServerPortTemplateAddView(ComponentTemplateCreateView):
     model = ConsoleServerPortTemplate
     form = forms.ConsoleServerPortTemplateForm
 
 
+class ConsoleServerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
+    permission_required = 'dcim.delete_consoleserverporttemplate'
+    cls = ConsoleServerPortTemplate
+    parent_cls = DeviceType
+
+
 class PowerPortTemplateAddView(ComponentTemplateCreateView):
     model = PowerPortTemplate
     form = forms.PowerPortTemplateForm
 
 
+class PowerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
+    permission_required = 'dcim.delete_powerporttemplate'
+    cls = PowerPortTemplate
+    parent_cls = DeviceType
+
+
 class PowerOutletTemplateAddView(ComponentTemplateCreateView):
     model = PowerOutletTemplate
     form = forms.PowerOutletTemplateForm
 
 
+class PowerOutletTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
+    permission_required = 'dcim.delete_poweroutlettemplate'
+    cls = PowerOutletTemplate
+    parent_cls = DeviceType
+
+
 class InterfaceTemplateAddView(ComponentTemplateCreateView):
     model = InterfaceTemplate
     form = forms.InterfaceTemplateForm
 
 
+class InterfaceTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
+    permission_required = 'dcim.delete_interfacetemplate'
+    cls = InterfaceTemplate
+    parent_cls = DeviceType
+
+
 class DeviceBayTemplateAddView(ComponentTemplateCreateView):
     model = DeviceBayTemplate
     form = forms.DeviceBayTemplateForm
 
 
-def component_template_delete(request, pk, model):
-
-    devicetype = get_object_or_404(DeviceType, pk=pk)
-
-    class ComponentTemplateBulkDeleteForm(ConfirmationForm):
-        pk = ModelMultipleChoiceField(queryset=model.objects.all(), widget=MultipleHiddenInput)
-
-    if '_confirm' in request.POST:
-        form = ComponentTemplateBulkDeleteForm(request.POST)
-        if form.is_valid():
-
-            # Delete component templates
-            objects_to_delete = model.objects.filter(pk__in=[v.id for v in form.cleaned_data['pk']])
-            try:
-                deleted_count = objects_to_delete.count()
-                objects_to_delete.delete()
-            except ProtectedError, e:
-                handle_protectederror(list(objects_to_delete), request, e)
-                return redirect('dcim:devicetype', {'pk': devicetype.pk})
-
-            messages.success(request, "Deleted {} {}".format(deleted_count, model._meta.verbose_name_plural))
-            return redirect('dcim:devicetype', pk=devicetype.pk)
-
-    else:
-        form = ComponentTemplateBulkDeleteForm(initial={'pk': request.POST.getlist('pk')})
-
-    selected_objects = model.objects.filter(pk__in=request.POST.getlist('pk'))
-    if not selected_objects:
-        messages.warning(request, "No {} were selected for deletion.".format(model._meta.verbose_name_plural))
-        return redirect('dcim:devicetype', pk=devicetype.pk)
-
-    return render(request, 'dcim/component_template_delete.html', {
-        'devicetype': devicetype,
-        'form': form,
-        'selected_objects': selected_objects,
-        'cancel_url': reverse('dcim:devicetype', kwargs={'pk': devicetype.pk}),
-    })
+class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
+    permission_required = 'dcim.delete_devicebaytemplate'
+    cls = DeviceBayTemplate
+    parent_cls = DeviceType
 
 
 #

+ 6 - 2
netbox/templates/utilities/confirm_bulk_delete.html

@@ -5,11 +5,15 @@
 
 {% block message %}
     <p>
-        Are you sure you want to delete these {{ obj_type_plural|default:"objects" }}?
+        Are you sure you want to delete these {{ obj_type_plural|default:"objects" }}{% if parent_obj %} from <a href="{{ parent_obj.get_absolute_url }}">{{ parent_obj }}</a>{% endif %}?
     </p>
     <ul>
         {% for obj in selected_objects %}
-            <li><a href="{{ obj.get_absolute_url }}">{{ obj }}</a></li>
+            {% if obj.get_absolute_url %}
+                <li><a href="{{ obj.get_absolute_url }}">{{ obj }}</a></li>
+            {% else %}
+                <li>{{ obj }}</li>
+            {% endif %}
         {% endfor %}
     </ul>
 {% endblock %}

+ 31 - 6
netbox/utilities/views.py

@@ -3,9 +3,11 @@ from django_tables2 import RequestConfig
 from django.contrib import messages
 from django.contrib.admin.views.decorators import staff_member_required
 from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import ImproperlyConfigured
 from django.core.urlresolvers import reverse
 from django.db import transaction, IntegrityError
 from django.db.models import ProtectedError
+from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
 from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import get_object_or_404, redirect, render
 from django.template import TemplateSyntaxError
@@ -309,6 +311,7 @@ class BulkEditView(View):
 
 class BulkDeleteView(View):
     cls = None
+    parent_cls = None
     form = None
     template_name = 'utilities/confirm_bulk_delete.html'
     default_redirect_url = None
@@ -317,24 +320,35 @@ class BulkDeleteView(View):
     def dispatch(self, *args, **kwargs):
         return super(BulkDeleteView, self).dispatch(*args, **kwargs)
 
-    def get(self, request, *args, **kwargs):
-        return redirect(self.default_redirect_url)
-
     def post(self, request, *args, **kwargs):
 
+        # Attempt to derive parent object if a parent class has been given
+        if self.parent_cls:
+            parent_obj = get_object_or_404(self.parent_cls, **kwargs)
+        else:
+            parent_obj = None
+
+        # Determine URL to redirect users upon deletion of objects
         posted_redirect_url = request.POST.get('redirect_url')
         if posted_redirect_url and is_safe_url(url=posted_redirect_url, host=request.get_host()):
             redirect_url = posted_redirect_url
-        else:
+        elif parent_obj:
+            redirect_url = parent_obj.get_absolute_url()
+        elif self.default_redirect_url:
             redirect_url = reverse(self.default_redirect_url)
+        else:
+            raise ImproperlyConfigured('No redirect URL has been provided.')
 
+        # Are we deleting *all* objects in the queryset or just a selected subset?
         if request.POST.get('_all'):
             pk_list = [x for x in request.POST.get('pk_all').split(',') if x]
         else:
             pk_list = request.POST.getlist('pk')
 
+        form_cls = self.get_form()
+
         if '_confirm' in request.POST:
-            form = self.form(request.POST)
+            form = form_cls(request.POST)
             if form.is_valid():
 
                 # Delete objects
@@ -351,7 +365,7 @@ class BulkDeleteView(View):
                 return redirect(redirect_url)
 
         else:
-            form = self.form(initial={'pk': pk_list})
+            form = form_cls(initial={'pk': pk_list})
 
         selected_objects = self.cls.objects.filter(pk__in=pk_list)
         if not selected_objects:
@@ -360,7 +374,18 @@ class BulkDeleteView(View):
 
         return render(request, self.template_name, {
             'form': form,
+            'parent_obj': parent_obj,
             'obj_type_plural': self.cls._meta.verbose_name_plural,
             'selected_objects': selected_objects,
             'cancel_url': redirect_url,
         })
+
+    def get_form(self):
+        """Provide a standard bulk delete form if none has been specified for the view"""
+
+        class BulkDeleteForm(ConfirmationForm):
+            pk = ModelMultipleChoiceField(queryset=self.cls.objects.all(), widget=MultipleHiddenInput)
+
+        if self.form:
+            return self.form
+        return BulkDeleteForm