Browse Source

Re-implemented method for bulk editing/deleting all objects within a filtered queryset

Jeremy Stretch 8 years ago
parent
commit
39d083eae7

+ 4 - 0
netbox/circuits/views.py

@@ -67,6 +67,7 @@ class ProviderBulkImportView(PermissionRequiredMixin, BulkImportView):
 class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'circuits.change_provider'
     cls = Provider
+    filter = filters.ProviderFilter
     form = forms.ProviderBulkEditForm
     template_name = 'circuits/provider_bulk_edit.html'
     default_redirect_url = 'circuits:provider_list'
@@ -75,6 +76,7 @@ class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
 class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'circuits.delete_provider'
     cls = Provider
+    filter = filters.ProviderFilter
     default_redirect_url = 'circuits:provider_list'
 
 
@@ -156,6 +158,7 @@ class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView):
 class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'circuits.change_circuit'
     cls = Circuit
+    filter = filters.CircuitFilter
     form = forms.CircuitBulkEditForm
     template_name = 'circuits/circuit_bulk_edit.html'
     default_redirect_url = 'circuits:circuit_list'
@@ -164,6 +167,7 @@ class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
 class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'circuits.delete_circuit'
     cls = Circuit
+    filter = filters.CircuitFilter
     default_redirect_url = 'circuits:circuit_list'
 
 

+ 9 - 1
netbox/dcim/views.py

@@ -177,6 +177,7 @@ class SiteBulkImportView(PermissionRequiredMixin, BulkImportView):
 class SiteBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_site'
     cls = Site
+    filter = filters.SiteFilter
     form = forms.SiteBulkEditForm
     template_name = 'dcim/site_bulk_edit.html'
     default_redirect_url = 'dcim:site_list'
@@ -207,6 +208,7 @@ class RackGroupEditView(PermissionRequiredMixin, ObjectEditView):
 class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_rackgroup'
     cls = RackGroup
+    filter = filters.RackGroupFilter
     default_redirect_url = 'dcim:rackgroup_list'
 
 
@@ -294,6 +296,7 @@ class RackBulkImportView(PermissionRequiredMixin, BulkImportView):
 class RackBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_rack'
     cls = Rack
+    filter = filters.RackFilter
     form = forms.RackBulkEditForm
     template_name = 'dcim/rack_bulk_edit.html'
     default_redirect_url = 'dcim:rack_list'
@@ -302,6 +305,7 @@ class RackBulkEditView(PermissionRequiredMixin, BulkEditView):
 class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_rack'
     cls = Rack
+    filter = filters.RackFilter
     default_redirect_url = 'dcim:rack_list'
 
 
@@ -410,6 +414,7 @@ class DeviceTypeDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 class DeviceTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_devicetype'
     cls = DeviceType
+    filter = filters.DeviceTypeFilter
     form = forms.DeviceTypeBulkEditForm
     template_name = 'dcim/devicetype_bulk_edit.html'
     default_redirect_url = 'dcim:devicetype_list'
@@ -418,6 +423,7 @@ class DeviceTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
 class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_devicetype'
     cls = DeviceType
+    filter = filters.DeviceTypeFilter
     default_redirect_url = 'dcim:devicetype_list'
 
 
@@ -703,6 +709,7 @@ class ChildDeviceBulkImportView(PermissionRequiredMixin, BulkImportView):
 class DeviceBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_device'
     cls = Device
+    filter = filters.DeviceFilter
     form = forms.DeviceBulkEditForm
     template_name = 'dcim/device_bulk_edit.html'
     default_redirect_url = 'dcim:device_list'
@@ -711,6 +718,7 @@ class DeviceBulkEditView(PermissionRequiredMixin, BulkEditView):
 class DeviceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_device'
     cls = Device
+    filter = filters.DeviceFilter
     default_redirect_url = 'dcim:device_list'
 
 
@@ -1245,7 +1253,7 @@ class DeviceBulkAddComponentView(View):
 
         # Are we editing *all* objects in the queryset or just a selected subset?
         if request.POST.get('_all'):
-            pk_list = [int(pk) for pk in request.POST.get('pk_all').split(',') if pk]
+            pk_list = [obj.pk for obj in filters.DeviceFilter(request.GET, Device.objects.all())]
         else:
             pk_list = [int(pk) for pk in request.POST.getlist('pk')]
 

+ 12 - 0
netbox/ipam/views.py

@@ -138,6 +138,7 @@ class VRFBulkImportView(PermissionRequiredMixin, BulkImportView):
 class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_vrf'
     cls = VRF
+    filter = filters.VRFFilter
     form = forms.VRFBulkEditForm
     template_name = 'ipam/vrf_bulk_edit.html'
     default_redirect_url = 'ipam:vrf_list'
@@ -146,6 +147,7 @@ class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
 class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_vrf'
     cls = VRF
+    filter = filters.VRFFilter
     default_redirect_url = 'ipam:vrf_list'
 
 
@@ -250,6 +252,7 @@ class RIREditView(PermissionRequiredMixin, ObjectEditView):
 class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_rir'
     cls = RIR
+    filter = filters.RIRFilter
     default_redirect_url = 'ipam:rir_list'
 
 
@@ -328,6 +331,7 @@ class AggregateBulkImportView(PermissionRequiredMixin, BulkImportView):
 class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_aggregate'
     cls = Aggregate
+    filter = filters.AggregateFilter
     form = forms.AggregateBulkEditForm
     template_name = 'ipam/aggregate_bulk_edit.html'
     default_redirect_url = 'ipam:aggregate_list'
@@ -336,6 +340,7 @@ class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
 class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_aggregate'
     cls = Aggregate
+    filter = filters.AggregateFilter
     default_redirect_url = 'ipam:aggregate_list'
 
 
@@ -460,6 +465,7 @@ class PrefixBulkImportView(PermissionRequiredMixin, BulkImportView):
 class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_prefix'
     cls = Prefix
+    filter = filters.PrefixFilter
     form = forms.PrefixBulkEditForm
     template_name = 'ipam/prefix_bulk_edit.html'
     default_redirect_url = 'ipam:prefix_list'
@@ -468,6 +474,7 @@ class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
 class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_prefix'
     cls = Prefix
+    filter = filters.PrefixFilter
     default_redirect_url = 'ipam:prefix_list'
 
 
@@ -648,6 +655,7 @@ class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
 class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_ipaddress'
     cls = IPAddress
+    filter = filters.IPAddressFilter
     form = forms.IPAddressBulkEditForm
     template_name = 'ipam/ipaddress_bulk_edit.html'
     default_redirect_url = 'ipam:ipaddress_list'
@@ -656,6 +664,7 @@ class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
 class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_ipaddress'
     cls = IPAddress
+    filter = filters.IPAddressFilter
     default_redirect_url = 'ipam:ipaddress_list'
 
 
@@ -684,6 +693,7 @@ class VLANGroupEditView(PermissionRequiredMixin, ObjectEditView):
 class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_vlangroup'
     cls = VLANGroup
+    filter = filters.VLANGroupFilter
     default_redirect_url = 'ipam:vlangroup_list'
 
 
@@ -737,6 +747,7 @@ class VLANBulkImportView(PermissionRequiredMixin, BulkImportView):
 class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_vlan'
     cls = VLAN
+    filter = filters.VLANFilter
     form = forms.VLANBulkEditForm
     template_name = 'ipam/vlan_bulk_edit.html'
     default_redirect_url = 'ipam:vlan_list'
@@ -745,6 +756,7 @@ class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
 class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_vlan'
     cls = VLAN
+    filter = filters.VLANFilter
     default_redirect_url = 'ipam:vlan_list'
 
 

+ 8 - 0
netbox/project-static/js/forms.js

@@ -9,6 +9,14 @@ $(document).ready(function() {
             $('#select_all').prop('checked', false);
         }
     });
+    // Enable hidden buttons when "select all" is checked
+    $('#select_all').click(function (event) {
+        if ($(this).is(':checked')) {
+            $('#select_all_box').find('button').prop('disabled', '');
+        } else {
+            $('#select_all_box').find('button').prop('disabled', 'disabled');
+        }
+    });
     // Uncheck the "toggle all" checkbox if an item is unchecked
     $('input:checkbox[name=pk]').click(function (event) {
         if (!$(this).attr('checked')) {

+ 2 - 0
netbox/secrets/views.py

@@ -202,6 +202,7 @@ def secret_import(request):
 class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'secrets.change_secret'
     cls = Secret
+    filter = filters.SecretFilter
     form = forms.SecretBulkEditForm
     template_name = 'secrets/secret_bulk_edit.html'
     default_redirect_url = 'secrets:secret_list'
@@ -210,4 +211,5 @@ class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
 class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'secrets.delete_secret'
     cls = Secret
+    filter = filters.SecretFilter
     default_redirect_url = 'secrets:secret_list'

+ 6 - 6
netbox/templates/dcim/inc/device_table.html

@@ -7,12 +7,12 @@
                 <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
             </button>
             <ul class="dropdown-menu">
-                {% if perms.dcim.add_consoleport %}<li><a href="{% url 'dcim:device_bulk_add_consoleport' %}" class="formaction">Console Ports</a></li>{% endif %}
-                {% if perms.dcim.add_consoleserverport %}<li><a href="{% url 'dcim:device_bulk_add_consoleserverport' %}" class="formaction">Console Server Ports</a></li>{% endif %}
-                {% if perms.dcim.add_powerport %}<li><a href="{% url 'dcim:device_bulk_add_powerport' %}" class="formaction">Power Ports</a></li>{% endif %}
-                {% if perms.dcim.add_poweroutlet %}<li><a href="{% url 'dcim:device_bulk_add_poweroutlet' %}" class="formaction">Power Outlets</a></li>{% endif %}
-                {% if perms.dcim.add_interface %}<li><a href="{% url 'dcim:device_bulk_add_interface' %}" class="formaction">Interfaces</a></li>{% endif %}
-                {% if perms.dcim.add_devicebay %}<li><a href="{% url 'dcim:device_bulk_add_devicebay' %}" class="formaction">Device Bays</a></li>{% endif %}
+                {% if perms.dcim.add_consoleport %}<li><a href="{% url 'dcim:device_bulk_add_consoleport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Console Ports</a></li>{% endif %}
+                {% if perms.dcim.add_consoleserverport %}<li><a href="{% url 'dcim:device_bulk_add_consoleserverport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Console Server Ports</a></li>{% endif %}
+                {% if perms.dcim.add_powerport %}<li><a href="{% url 'dcim:device_bulk_add_powerport' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Power Ports</a></li>{% endif %}
+                {% if perms.dcim.add_poweroutlet %}<li><a href="{% url 'dcim:device_bulk_add_poweroutlet' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Power Outlets</a></li>{% endif %}
+                {% if perms.dcim.add_interface %}<li><a href="{% url 'dcim:device_bulk_add_interface' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Interfaces</a></li>{% endif %}
+                {% if perms.dcim.add_devicebay %}<li><a href="{% url 'dcim:device_bulk_add_devicebay' %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="formaction">Device Bays</a></li>{% endif %}
             </ul>
         </div>
     {% endif %}

+ 1 - 1
netbox/templates/utilities/confirm_bulk_delete.html

@@ -5,7 +5,7 @@
 
 {% block message %}
     <p>
-        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 %}?
+        Are you sure you want to delete these {{ selected_objects|length }} {{ 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 %}

+ 22 - 9
netbox/templates/utilities/obj_table.html

@@ -4,26 +4,39 @@
     <form method="post" class="form form-horizontal">
         {% csrf_token %}
         <input type="hidden" name="redirect_url" value="{{ request.path }}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" />
-        <input type="hidden" name="pk_all" value="{% for obj in table.data.queryset %}{{ obj.pk|default:'' }}{% if not forloop.last %},{% endif %}{% endfor %}" />
         {% if table.paginator.num_pages > 1 %}
-            <div id="select_all_box" class="hidden alert alert-info">
-                <div class="checkbox-inline">
-                    <label for="select_all">
-                        <input type="checkbox" id="select_all" name="_all" />
-                        Select <strong>all {{ table.rows|length }} {{ table.data.verbose_name_plural }}</strong> matching query
-                    </label>
+            <div id="select_all_box" class="hidden panel panel-default">
+                <div class="panel-body">
+                    <div class="checkbox-inline">
+                        <label for="select_all">
+                            <input type="checkbox" id="select_all" name="_all" />
+                            Select <strong>all {{ table.rows|length }} {{ table.data.verbose_name_plural }}</strong> matching query
+                        </label>
+                    </div>
+                    <div class="pull-right">
+                        {% if bulk_edit_url and table.model|user_can_change:request.user %}
+                            <button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled="disabled">
+                                <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit All
+                            </button>
+                        {% endif %}
+                        {% if bulk_delete_url and table.model|user_can_delete:request.user %}
+                            <button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled="disabled">
+                                <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete All
+                            </button>
+                        {% endif %}
+                    </div>
                 </div>
             </div>
         {% endif %}
         {% render_table table table_template|default:'table.html' %}
         {% block extra_actions %}{% endblock %}
         {% if bulk_edit_url and table.model|user_can_change:request.user %}
-            <button type="submit" name="_edit" formaction="{% url bulk_edit_url %}" class="btn btn-warning btn-sm">
+            <button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm">
                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit Selected
             </button>
         {% endif %}
         {% if bulk_delete_url and table.model|user_can_delete:request.user %}
-            <button type="submit" name="_delete" formaction="{% url bulk_delete_url %}" class="btn btn-danger btn-sm">
+            <button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm">
                 <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete Selected
             </button>
         {% endif %}

+ 2 - 0
netbox/tenancy/views.py

@@ -105,6 +105,7 @@ class TenantBulkImportView(PermissionRequiredMixin, BulkImportView):
 class TenantBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'tenancy.change_tenant'
     cls = Tenant
+    filter = filters.TenantFilter
     form = forms.TenantBulkEditForm
     template_name = 'tenancy/tenant_bulk_edit.html'
     default_redirect_url = 'tenancy:tenant_list'
@@ -113,4 +114,5 @@ class TenantBulkEditView(PermissionRequiredMixin, BulkEditView):
 class TenantBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'tenancy.delete_tenant'
     cls = Tenant
+    filter = filters.TenantFilter
     default_redirect_url = 'tenancy:tenant_list'

+ 8 - 4
netbox/utilities/views.py

@@ -397,12 +397,14 @@ class BulkEditView(View):
 
     cls: The model of the objects being edited
     parent_cls: The model of the parent object (if any)
+    filter: FilterSet to apply when deleting by QuerySet
     form: The form class used to edit objects in bulk
     template_name: The name of the template
     default_redirect_url: Name of the URL to which the user is redirected after editing the objects
     """
     cls = None
     parent_cls = None
+    filter = None
     form = None
     template_name = None
     default_redirect_url = None
@@ -430,8 +432,8 @@ class BulkEditView(View):
             raise ImproperlyConfigured('No redirect URL has been provided.')
 
         # Are we editing *all* objects in the queryset or just a selected subset?
-        if request.POST.get('_all'):
-            pk_list = [int(pk) for pk in request.POST.get('pk_all').split(',') if pk]
+        if request.POST.get('_all') and self.filter is not None:
+            pk_list = [obj.pk for obj in self.filter(request.GET, self.cls.objects.only('pk'))]
         else:
             pk_list = [int(pk) for pk in request.POST.getlist('pk')]
 
@@ -535,12 +537,14 @@ class BulkDeleteView(View):
 
     cls: The model of the objects being deleted
     parent_cls: The model of the parent object (if any)
+    filter: FilterSet to apply when deleting by QuerySet
     form: The form class used to delete objects in bulk
     template_name: The name of the template
     default_redirect_url: Name of the URL to which the user is redirected after deleting the objects
     """
     cls = None
     parent_cls = None
+    filter = None
     form = None
     template_name = 'utilities/confirm_bulk_delete.html'
     default_redirect_url = None
@@ -565,8 +569,8 @@ class BulkDeleteView(View):
             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 = [int(pk) for pk in request.POST.get('pk_all').split(',') if pk]
+        if request.POST.get('_all') and self.filter is not None:
+            pk_list = [obj.pk for obj in self.filter(request.GET, self.cls.objects.only('pk'))]
         else:
             pk_list = [int(pk) for pk in request.POST.getlist('pk')]