1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

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

This commit is contained in:
Jeremy Stretch
2016-07-25 16:32:07 -04:00
parent d241cce502
commit d47bf4ab6b
5 changed files with 103 additions and 73 deletions

View File

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

View File

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

View File

@ -7,8 +7,7 @@ from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Count, ProtectedError, Sum from django.db.models import Count, Sum
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.http import urlencode from django.utils.http import urlencode
@ -17,7 +16,6 @@ from django.views.generic import View
from ipam.models import Prefix, IPAddress, VLAN from ipam.models import Prefix, IPAddress, VLAN
from circuits.models import Circuit from circuits.models import Circuit
from extras.models import TopologyMap from extras.models import TopologyMap
from utilities.error_handlers import handle_protectederror
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.views import ( from utilities.views import (
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
@ -396,68 +394,65 @@ class ConsolePortTemplateAddView(ComponentTemplateCreateView):
form = forms.ConsolePortTemplateForm form = forms.ConsolePortTemplateForm
class ConsolePortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_consoleporttemplate'
cls = ConsolePortTemplate
parent_cls = DeviceType
class ConsoleServerPortTemplateAddView(ComponentTemplateCreateView): class ConsoleServerPortTemplateAddView(ComponentTemplateCreateView):
model = ConsoleServerPortTemplate model = ConsoleServerPortTemplate
form = forms.ConsoleServerPortTemplateForm form = forms.ConsoleServerPortTemplateForm
class ConsoleServerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_consoleserverporttemplate'
cls = ConsoleServerPortTemplate
parent_cls = DeviceType
class PowerPortTemplateAddView(ComponentTemplateCreateView): class PowerPortTemplateAddView(ComponentTemplateCreateView):
model = PowerPortTemplate model = PowerPortTemplate
form = forms.PowerPortTemplateForm form = forms.PowerPortTemplateForm
class PowerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_powerporttemplate'
cls = PowerPortTemplate
parent_cls = DeviceType
class PowerOutletTemplateAddView(ComponentTemplateCreateView): class PowerOutletTemplateAddView(ComponentTemplateCreateView):
model = PowerOutletTemplate model = PowerOutletTemplate
form = forms.PowerOutletTemplateForm form = forms.PowerOutletTemplateForm
class PowerOutletTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_poweroutlettemplate'
cls = PowerOutletTemplate
parent_cls = DeviceType
class InterfaceTemplateAddView(ComponentTemplateCreateView): class InterfaceTemplateAddView(ComponentTemplateCreateView):
model = InterfaceTemplate model = InterfaceTemplate
form = forms.InterfaceTemplateForm form = forms.InterfaceTemplateForm
class InterfaceTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_interfacetemplate'
cls = InterfaceTemplate
parent_cls = DeviceType
class DeviceBayTemplateAddView(ComponentTemplateCreateView): class DeviceBayTemplateAddView(ComponentTemplateCreateView):
model = DeviceBayTemplate model = DeviceBayTemplate
form = forms.DeviceBayTemplateForm form = forms.DeviceBayTemplateForm
def component_template_delete(request, pk, model): class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_devicebaytemplate'
devicetype = get_object_or_404(DeviceType, pk=pk) cls = DeviceBayTemplate
parent_cls = DeviceType
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}),
})
# #

View File

@ -5,11 +5,15 @@
{% block message %} {% block message %}
<p> <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> </p>
<ul> <ul>
{% for obj in selected_objects %} {% 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 %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}

View File

@ -3,9 +3,11 @@ from django_tables2 import RequestConfig
from django.contrib import messages from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction, IntegrityError from django.db import transaction, IntegrityError
from django.db.models import ProtectedError from django.db.models import ProtectedError
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.template import TemplateSyntaxError from django.template import TemplateSyntaxError
@ -309,6 +311,7 @@ class BulkEditView(View):
class BulkDeleteView(View): class BulkDeleteView(View):
cls = None cls = None
parent_cls = None
form = None form = None
template_name = 'utilities/confirm_bulk_delete.html' template_name = 'utilities/confirm_bulk_delete.html'
default_redirect_url = None default_redirect_url = None
@ -317,24 +320,35 @@ class BulkDeleteView(View):
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
return super(BulkDeleteView, self).dispatch(*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): 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') posted_redirect_url = request.POST.get('redirect_url')
if posted_redirect_url and is_safe_url(url=posted_redirect_url, host=request.get_host()): if posted_redirect_url and is_safe_url(url=posted_redirect_url, host=request.get_host()):
redirect_url = posted_redirect_url 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) 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'): if request.POST.get('_all'):
pk_list = [x for x in request.POST.get('pk_all').split(',') if x] pk_list = [x for x in request.POST.get('pk_all').split(',') if x]
else: else:
pk_list = request.POST.getlist('pk') pk_list = request.POST.getlist('pk')
form_cls = self.get_form()
if '_confirm' in request.POST: if '_confirm' in request.POST:
form = self.form(request.POST) form = form_cls(request.POST)
if form.is_valid(): if form.is_valid():
# Delete objects # Delete objects
@ -351,7 +365,7 @@ class BulkDeleteView(View):
return redirect(redirect_url) return redirect(redirect_url)
else: 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) selected_objects = self.cls.objects.filter(pk__in=pk_list)
if not selected_objects: if not selected_objects:
@ -360,7 +374,18 @@ class BulkDeleteView(View):
return render(request, self.template_name, { return render(request, self.template_name, {
'form': form, 'form': form,
'parent_obj': parent_obj,
'obj_type_plural': self.cls._meta.verbose_name_plural, 'obj_type_plural': self.cls._meta.verbose_name_plural,
'selected_objects': selected_objects, 'selected_objects': selected_objects,
'cancel_url': redirect_url, '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