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

Added 'select all' option to object lists for bulk edit/delete

This commit is contained in:
Jeremy Stretch
2016-06-01 13:30:33 -04:00
parent 7163e6e29a
commit b4619fad7a
8 changed files with 63 additions and 33 deletions

View File

@ -1,6 +1,8 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from utilities.tables import ToggleColumn
from .models import Circuit, CircuitType, Provider
@ -16,7 +18,7 @@ CIRCUITTYPE_EDIT_LINK = """
#
class ProviderTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn('circuits:provider', args=[Accessor('slug')], verbose_name='Name')
asn = tables.Column(verbose_name='ASN')
circuit_count = tables.Column(accessor=Accessor('count_circuits'), verbose_name='Circuits')
@ -35,7 +37,7 @@ class ProviderTable(tables.Table):
#
class CircuitTypeTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
circuit_count = tables.Column(verbose_name='Circuits')
slug = tables.Column(verbose_name='Slug')
@ -55,7 +57,7 @@ class CircuitTypeTable(tables.Table):
#
class CircuitTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
cid = tables.LinkColumn('circuits:circuit', args=[Accessor('pk')], verbose_name='ID')
type = tables.Column(verbose_name='Type')
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')], verbose_name='Provider')

View File

@ -1,6 +1,8 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from utilities.tables import ToggleColumn
from .models import (
ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType, InterfaceTemplate,
Manufacturer, Platform, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, Site,
@ -75,7 +77,7 @@ class SiteTable(tables.Table):
#
class RackGroupTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
rack_count = tables.Column(verbose_name='Racks')
@ -96,7 +98,7 @@ class RackGroupTable(tables.Table):
#
class RackTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn('dcim:rack', args=[Accessor('pk')], verbose_name='Name')
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group')
@ -118,7 +120,7 @@ class RackTable(tables.Table):
#
class ManufacturerTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
devicetype_count = tables.Column(verbose_name='Device Types')
slug = tables.Column(verbose_name='Slug')
@ -138,7 +140,7 @@ class ManufacturerTable(tables.Table):
#
class DeviceTypeTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type')
class Meta:
@ -155,7 +157,7 @@ class DeviceTypeTable(tables.Table):
#
class ConsolePortTemplateTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
class Meta:
model = ConsolePortTemplate
@ -168,7 +170,7 @@ class ConsolePortTemplateTable(tables.Table):
class ConsoleServerPortTemplateTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
class Meta:
model = ConsoleServerPortTemplate
@ -181,7 +183,7 @@ class ConsoleServerPortTemplateTable(tables.Table):
class PowerPortTemplateTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
class Meta:
model = PowerPortTemplate
@ -194,7 +196,7 @@ class PowerPortTemplateTable(tables.Table):
class PowerOutletTemplateTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
class Meta:
model = PowerOutletTemplate
@ -207,7 +209,7 @@ class PowerOutletTemplateTable(tables.Table):
class InterfaceTemplateTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
class Meta:
model = InterfaceTemplate
@ -224,7 +226,7 @@ class InterfaceTemplateTable(tables.Table):
#
class DeviceRoleTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
device_count = tables.Column(verbose_name='Devices')
slug = tables.Column(verbose_name='Slug')
@ -245,7 +247,7 @@ class DeviceRoleTable(tables.Table):
#
class PlatformTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
device_count = tables.Column(verbose_name='Devices')
slug = tables.Column(verbose_name='Slug')
@ -265,7 +267,7 @@ class PlatformTable(tables.Table):
#
class DeviceTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
status = tables.TemplateColumn(template_code=STATUS_ICON, verbose_name='')
name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name')
site = tables.Column(accessor=Accessor('rack.site'), verbose_name='Site')

View File

@ -1,6 +1,8 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from utilities.tables import ToggleColumn
from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VRF
@ -54,7 +56,7 @@ STATUS_LABEL = """
#
class VRFTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn('ipam:vrf', args=[Accessor('pk')], verbose_name='Name')
rd = tables.Column(verbose_name='RD')
description = tables.Column(sortable=False, verbose_name='Description')
@ -73,7 +75,7 @@ class VRFTable(tables.Table):
#
class RIRTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
aggregate_count = tables.Column(verbose_name='Aggregates')
slug = tables.Column(verbose_name='Slug')
@ -93,7 +95,7 @@ class RIRTable(tables.Table):
#
class AggregateTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
prefix = tables.LinkColumn('ipam:aggregate', args=[Accessor('pk')], verbose_name='Aggregate')
rir = tables.Column(verbose_name='RIR')
child_count = tables.Column(verbose_name='Prefixes')
@ -115,7 +117,7 @@ class AggregateTable(tables.Table):
#
class RoleTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.Column(verbose_name='Name')
prefix_count = tables.Column(accessor=Accessor('count_prefixes'), orderable=False, verbose_name='Prefixes')
vlan_count = tables.Column(accessor=Accessor('count_vlans'), orderable=False, verbose_name='VLANs')
@ -136,7 +138,7 @@ class RoleTable(tables.Table):
#
class PrefixTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
prefix = tables.TemplateColumn(PREFIX_LINK, verbose_name='Prefix')
vrf = tables.Column(orderable=False, default='Global', verbose_name='VRF')
@ -173,7 +175,7 @@ class PrefixBriefTable(tables.Table):
#
class IPAddressTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
address = tables.LinkColumn('ipam:ipaddress', args=[Accessor('pk')], verbose_name='IP Address')
vrf = tables.Column(orderable=False, default='Global', verbose_name='VRF')
device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False,
@ -212,7 +214,7 @@ class IPAddressBriefTable(tables.Table):
#
class VLANTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
name = tables.Column(verbose_name='Name')

View File

@ -1,6 +1,8 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from utilities.tables import ToggleColumn
from .models import SecretRole, Secret
@ -16,7 +18,7 @@ SECRETROLE_EDIT_LINK = """
#
class SecretRoleTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
secret_count = tables.Column(verbose_name='Secrets')
slug = tables.Column(verbose_name='Slug')
@ -36,7 +38,7 @@ class SecretRoleTable(tables.Table):
#
class SecretTable(tables.Table):
pk = tables.CheckBoxColumn(visible=False, default='')
pk = ToggleColumn()
device = tables.LinkColumn('secrets:secret', args=[Accessor('pk')], verbose_name='Device')
role = tables.Column(verbose_name='Role')
name = tables.Column(verbose_name='Name')

View File

@ -4,6 +4,7 @@
<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 row in table.rows %}{{ row.record.pk }}{% if not forloop.last %},{% endif %}{% endfor %}" />
{% render_table table table_template|default:'table.html' %}
{% if perms.dcim.add_interface %}
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_add' %}" class="btn btn-primary btn-sm">

View File

@ -4,6 +4,7 @@
<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 row in table.rows %}{{ row.record.pk }}{% if not forloop.last %},{% endif %}{% endfor %}" />
{% render_table table table_template|default:'table.html' %}
{% 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">

View File

@ -0,0 +1,12 @@
import django_tables2 as tables
from django.utils.safestring import mark_safe
class ToggleColumn(tables.CheckBoxColumn):
default = ''
visible = False
@property
def header(self):
return mark_safe('<input type="checkbox" name="_all" title="Select all" />')

View File

@ -260,10 +260,14 @@ class BulkEditView(View):
else:
redirect_url = reverse(self.default_redirect_url)
if request.POST.get('_all'):
pk_list = request.POST.get('pk_all').split(',')
else:
pk_list = request.POST.getlist('pk')
if '_apply' in request.POST:
form = self.form(request.POST)
if form.is_valid():
pk_list = [obj.pk for obj in form.cleaned_data['pk']]
updated_count = self.update_objects(pk_list, form)
msg = 'Updated {} {}'.format(updated_count, self.cls._meta.verbose_name_plural)
messages.success(self.request, msg)
@ -272,9 +276,9 @@ class BulkEditView(View):
return redirect(redirect_url)
else:
form = self.form(initial={'pk': request.POST.getlist('pk')})
form = self.form(initial={'pk': pk_list})
selected_objects = self.cls.objects.filter(pk__in=request.POST.getlist('pk'))
selected_objects = self.cls.objects.filter(pk__in=pk_list)
if not selected_objects:
messages.warning(request, "No {} were selected.".format(self.cls._meta.verbose_name_plural))
return redirect(redirect_url)
@ -313,17 +317,21 @@ class BulkDeleteView(View):
else:
redirect_url = reverse(self.default_redirect_url)
if request.POST.get('_all'):
pk_list = request.POST.get('pk_all').split(',')
else:
pk_list = request.POST.getlist('pk')
if '_confirm' in request.POST:
form = self.form(request.POST)
if form.is_valid():
# Delete objects
objects_to_delete = self.cls.objects.filter(pk__in=[v.id for v in form.cleaned_data['pk']])
queryset = self.cls.objects.filter(pk__in=pk_list)
try:
deleted_count = objects_to_delete.count()
objects_to_delete.delete()
deleted_count = queryset.delete()[0]
except ProtectedError, e:
handle_protectederror(list(objects_to_delete), request, e)
handle_protectederror(list(queryset), request, e)
return redirect(redirect_url)
msg = 'Deleted {} {}'.format(deleted_count, self.cls._meta.verbose_name_plural)
@ -332,9 +340,9 @@ class BulkDeleteView(View):
return redirect(redirect_url)
else:
form = self.form(initial={'pk': request.POST.getlist('pk')})
form = self.form(initial={'pk': pk_list})
selected_objects = self.cls.objects.filter(pk__in=form.initial.get('pk'))
selected_objects = self.cls.objects.filter(pk__in=pk_list)
if not selected_objects:
messages.warning(request, "No {} were selected for deletion.".format(self.cls._meta.verbose_name_plural))
return redirect(redirect_url)