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

Optimized performance when editing/deleting objects in bulk

This commit is contained in:
Jeremy Stretch
2017-07-13 17:39:28 -04:00
parent dd1991f2c6
commit 39730b6834
8 changed files with 85 additions and 32 deletions

View File

@ -117,6 +117,7 @@ class CircuitTypeEditView(CircuitTypeCreateView):
class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'circuits.delete_circuittype'
cls = CircuitType
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
table = tables.CircuitTypeTable
default_return_url = 'circuits:circuittype_list'
@ -184,6 +185,7 @@ class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView):
class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'circuits.change_circuit'
cls = Circuit
queryset = Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
filter = filters.CircuitFilter
table = tables.CircuitTable
form = forms.CircuitBulkEditForm
@ -193,6 +195,7 @@ class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'circuits.delete_circuit'
cls = Circuit
queryset = Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
filter = filters.CircuitFilter
table = tables.CircuitTable
default_return_url = 'circuits:circuit_list'

View File

@ -285,19 +285,10 @@ class DeviceTypeTable(BaseTable):
is_pdu = tables.BooleanColumn(verbose_name='PDU')
is_network_device = tables.BooleanColumn(verbose_name='Net')
subdevice_role = tables.TemplateColumn(SUBDEVICE_ROLE_TEMPLATE, verbose_name='Subdevice Role')
instance_count = tables.Column(verbose_name='Instances')
class Meta(BaseTable.Meta):
model = DeviceType
fields = (
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
'is_network_device', 'subdevice_role',
)
class DeviceTypeDetailTable(DeviceTypeTable):
instance_count = tables.Column(verbose_name='Instances')
class Meta(DeviceTypeTable.Meta):
fields = (
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu',
'is_network_device', 'subdevice_role', 'instance_count',
@ -315,7 +306,6 @@ class ConsolePortTemplateTable(BaseTable):
model = ConsolePortTemplate
fields = ('pk', 'name')
empty_text = "None"
show_header = False
class ConsoleServerPortTemplateTable(BaseTable):
@ -325,7 +315,6 @@ class ConsoleServerPortTemplateTable(BaseTable):
model = ConsoleServerPortTemplate
fields = ('pk', 'name')
empty_text = "None"
show_header = False
class PowerPortTemplateTable(BaseTable):
@ -335,7 +324,6 @@ class PowerPortTemplateTable(BaseTable):
model = PowerPortTemplate
fields = ('pk', 'name')
empty_text = "None"
show_header = False
class PowerOutletTemplateTable(BaseTable):
@ -345,7 +333,6 @@ class PowerOutletTemplateTable(BaseTable):
model = PowerOutletTemplate
fields = ('pk', 'name')
empty_text = "None"
show_header = False
class InterfaceTemplateTable(BaseTable):
@ -356,7 +343,6 @@ class InterfaceTemplateTable(BaseTable):
model = InterfaceTemplate
fields = ('pk', 'name', 'mgmt_only', 'form_factor')
empty_text = "None"
show_header = False
class DeviceBayTemplateTable(BaseTable):
@ -366,7 +352,6 @@ class DeviceBayTemplateTable(BaseTable):
model = DeviceBayTemplate
fields = ('pk', 'name')
empty_text = "None"
show_header = False
#

View File

@ -205,6 +205,7 @@ class RegionEditView(RegionCreateView):
class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_region'
cls = Region
queryset = Region.objects.annotate(site_count=Count('sites'))
table = tables.RegionTable
default_return_url = 'dcim:region_list'
@ -274,6 +275,7 @@ class SiteBulkImportView(PermissionRequiredMixin, BulkImportView):
class SiteBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_site'
cls = Site
queryset = Site.objects.select_related('region', 'tenant')
filter = filters.SiteFilter
table = tables.SiteTable
form = forms.SiteBulkEditForm
@ -308,6 +310,7 @@ class RackGroupEditView(RackGroupCreateView):
class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_rackgroup'
cls = RackGroup
queryset = RackGroup.objects.select_related('site').annotate(rack_count=Count('racks'))
filter = filters.RackGroupFilter
table = tables.RackGroupTable
default_return_url = 'dcim:rackgroup_list'
@ -339,6 +342,7 @@ class RackRoleEditView(RackRoleCreateView):
class RackRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_rackrole'
cls = RackRole
queryset = RackRole.objects.annotate(rack_count=Count('racks'))
table = tables.RackRoleTable
default_return_url = 'dcim:rackrole_list'
@ -458,6 +462,7 @@ class RackBulkImportView(PermissionRequiredMixin, BulkImportView):
class RackBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_rack'
cls = Rack
queryset = Rack.objects.select_related('site', 'group', 'tenant', 'role')
filter = filters.RackFilter
table = tables.RackTable
form = forms.RackBulkEditForm
@ -467,6 +472,7 @@ class RackBulkEditView(PermissionRequiredMixin, BulkEditView):
class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_rack'
cls = Rack
queryset = Rack.objects.select_related('site', 'group', 'tenant', 'role')
filter = filters.RackFilter
table = tables.RackTable
default_return_url = 'dcim:rack_list'
@ -544,6 +550,7 @@ class ManufacturerEditView(ManufacturerCreateView):
class ManufacturerBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_manufacturer'
cls = Manufacturer
queryset = Manufacturer.objects.annotate(devicetype_count=Count('device_types'))
table = tables.ManufacturerTable
default_return_url = 'dcim:manufacturer_list'
@ -556,7 +563,7 @@ class DeviceTypeListView(ObjectListView):
queryset = DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances'))
filter = filters.DeviceTypeFilter
filter_form = forms.DeviceTypeFilterForm
table = tables.DeviceTypeDetailTable
table = tables.DeviceTypeTable
template_name = 'dcim/devicetype_list.html'
@ -568,24 +575,30 @@ class DeviceTypeView(View):
# Component tables
consoleport_table = tables.ConsolePortTemplateTable(
natsorted(ConsolePortTemplate.objects.filter(device_type=devicetype), key=attrgetter('name'))
natsorted(ConsolePortTemplate.objects.filter(device_type=devicetype), key=attrgetter('name')),
show_header=False
)
consoleserverport_table = tables.ConsoleServerPortTemplateTable(
natsorted(ConsoleServerPortTemplate.objects.filter(device_type=devicetype), key=attrgetter('name'))
natsorted(ConsoleServerPortTemplate.objects.filter(device_type=devicetype), key=attrgetter('name')),
show_header=False
)
powerport_table = tables.PowerPortTemplateTable(
natsorted(PowerPortTemplate.objects.filter(device_type=devicetype), key=attrgetter('name'))
natsorted(PowerPortTemplate.objects.filter(device_type=devicetype), key=attrgetter('name')),
show_header=False
)
poweroutlet_table = tables.PowerOutletTemplateTable(
natsorted(PowerOutletTemplate.objects.filter(device_type=devicetype), key=attrgetter('name'))
natsorted(PowerOutletTemplate.objects.filter(device_type=devicetype), key=attrgetter('name')),
show_header=False
)
interface_table = tables.InterfaceTemplateTable(
list(InterfaceTemplate.objects.order_naturally(
devicetype.interface_ordering
).filter(device_type=devicetype))
).filter(device_type=devicetype)),
show_header=False
)
devicebay_table = tables.DeviceBayTemplateTable(
natsorted(DeviceBayTemplate.objects.filter(device_type=devicetype), key=attrgetter('name'))
natsorted(DeviceBayTemplate.objects.filter(device_type=devicetype), key=attrgetter('name')),
show_header=False
)
if request.user.has_perm('dcim.change_devicetype'):
consoleport_table.base_columns['pk'].visible = True
@ -627,6 +640,7 @@ class DeviceTypeDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class DeviceTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_devicetype'
cls = DeviceType
queryset = DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances'))
filter = filters.DeviceTypeFilter
table = tables.DeviceTypeTable
form = forms.DeviceTypeBulkEditForm
@ -636,6 +650,7 @@ class DeviceTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_devicetype'
cls = DeviceType
queryset = DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances'))
filter = filters.DeviceTypeFilter
table = tables.DeviceTypeTable
default_return_url = 'dcim:devicetype_list'
@ -777,6 +792,7 @@ class DeviceRoleEditView(DeviceRoleCreateView):
class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_devicerole'
cls = DeviceRole
queryset = DeviceRole.objects.annotate(device_count=Count('devices'))
table = tables.DeviceRoleTable
default_return_url = 'dcim:devicerole_list'
@ -807,6 +823,7 @@ class PlatformEditView(PlatformCreateView):
class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_platform'
cls = Platform
queryset = Platform.objects.annotate(device_count=Count('devices'))
table = tables.PlatformTable
default_return_url = 'dcim:platform_list'
@ -971,6 +988,7 @@ class ChildDeviceBulkImportView(PermissionRequiredMixin, BulkImportView):
class DeviceBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_device'
cls = Device
queryset = Device.objects.select_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
filter = filters.DeviceFilter
table = tables.DeviceTable
form = forms.DeviceBulkEditForm
@ -980,6 +998,7 @@ class DeviceBulkEditView(PermissionRequiredMixin, BulkEditView):
class DeviceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_device'
cls = Device
queryset = Device.objects.select_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
filter = filters.DeviceFilter
table = tables.DeviceTable
default_return_url = 'dcim:device_list'

View File

@ -161,6 +161,14 @@ class RIRTable(BaseTable):
name = tables.LinkColumn(verbose_name='Name')
is_private = tables.BooleanColumn(verbose_name='Private')
aggregate_count = tables.Column(verbose_name='Aggregates')
actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
class Meta(BaseTable.Meta):
model = RIR
fields = ('pk', 'name', 'is_private', 'aggregate_count', 'actions')
class RIRDetailTable(RIRTable):
stats_total = tables.Column(accessor='stats.total', verbose_name='Total',
footer=lambda table: sum(r.stats['total'] for r in table.data))
stats_active = tables.Column(accessor='stats.active', verbose_name='Active',
@ -172,12 +180,12 @@ class RIRTable(BaseTable):
stats_available = tables.Column(accessor='stats.available', verbose_name='Available',
footer=lambda table: sum(r.stats['available'] for r in table.data))
utilization = tables.TemplateColumn(template_code=RIR_UTILIZATION, verbose_name='Utilization')
actions = tables.TemplateColumn(template_code=RIR_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='')
class Meta(BaseTable.Meta):
model = RIR
fields = ('pk', 'name', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved',
'stats_deprecated', 'stats_available', 'utilization', 'actions')
class Meta(RIRTable.Meta):
fields = (
'pk', 'name', 'is_private', 'aggregate_count', 'stats_total', 'stats_active', 'stats_reserved',
'stats_deprecated', 'stats_available', 'utilization', 'actions',
)
#

View File

@ -142,6 +142,7 @@ class VRFBulkImportView(PermissionRequiredMixin, BulkImportView):
class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'ipam.change_vrf'
cls = VRF
queryset = VRF.objects.select_related('tenant')
filter = filters.VRFFilter
table = tables.VRFTable
form = forms.VRFBulkEditForm
@ -151,7 +152,9 @@ class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_vrf'
cls = VRF
queryset = VRF.objects.select_related('tenant')
filter = filters.VRFFilter
table = tables.VRFTable
default_return_url = 'ipam:vrf_list'
@ -163,7 +166,7 @@ class RIRListView(ObjectListView):
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates'))
filter = filters.RIRFilter
filter_form = forms.RIRFilterForm
table = tables.RIRTable
table = tables.RIRDetailTable
template_name = 'ipam/rir_list.html'
def alter_queryset(self, request):
@ -259,7 +262,9 @@ class RIREditView(RIRCreateView):
class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_rir'
cls = RIR
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates'))
filter = filters.RIRFilter
table = tables.RIRTable
default_return_url = 'ipam:rir_list'
@ -360,6 +365,7 @@ class AggregateBulkImportView(PermissionRequiredMixin, BulkImportView):
class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'ipam.change_aggregate'
cls = Aggregate
queryset = Aggregate.objects.select_related('rir')
filter = filters.AggregateFilter
table = tables.AggregateTable
form = forms.AggregateBulkEditForm
@ -369,7 +375,9 @@ class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_aggregate'
cls = Aggregate
queryset = Aggregate.objects.select_related('rir')
filter = filters.AggregateFilter
table = tables.AggregateTable
default_return_url = 'ipam:aggregate_list'
@ -399,6 +407,7 @@ class RoleEditView(RoleCreateView):
class RoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_role'
cls = Role
table = tables.RoleTable
default_return_url = 'ipam:role_list'
@ -564,6 +573,7 @@ class PrefixBulkImportView(PermissionRequiredMixin, BulkImportView):
class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'ipam.change_prefix'
cls = Prefix
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
filter = filters.PrefixFilter
table = tables.PrefixTable
form = forms.PrefixBulkEditForm
@ -573,7 +583,9 @@ class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_prefix'
cls = Prefix
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
filter = filters.PrefixFilter
table = tables.PrefixTable
default_return_url = 'ipam:prefix_list'
@ -669,6 +681,7 @@ class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'ipam.change_ipaddress'
cls = IPAddress
queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device')
filter = filters.IPAddressFilter
table = tables.IPAddressTable
form = forms.IPAddressBulkEditForm
@ -678,7 +691,9 @@ class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_ipaddress'
cls = IPAddress
queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device')
filter = filters.IPAddressFilter
table = tables.IPAddressTable
default_return_url = 'ipam:ipaddress_list'
@ -710,7 +725,9 @@ class VLANGroupEditView(VLANGroupCreateView):
class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_vlangroup'
cls = VLANGroup
queryset = VLANGroup.objects.select_related('site').annotate(vlan_count=Count('vlans'))
filter = filters.VLANGroupFilter
table = tables.VLANGroupTable
default_return_url = 'ipam:vlangroup_list'
@ -771,6 +788,7 @@ class VLANBulkImportView(PermissionRequiredMixin, BulkImportView):
class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'ipam.change_vlan'
cls = VLAN
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
filter = filters.VLANFilter
table = tables.VLANTable
form = forms.VLANBulkEditForm
@ -780,7 +798,9 @@ class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'ipam.delete_vlan'
cls = VLAN
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
filter = filters.VLANFilter
table = tables.VLANTable
default_return_url = 'ipam:vlan_list'

View File

@ -55,6 +55,8 @@ class SecretRoleEditView(SecretRoleCreateView):
class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'secrets.delete_secretrole'
cls = SecretRole
queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
table = tables.SecretRoleTable
default_return_url = 'secrets:secretrole_list'
@ -239,6 +241,7 @@ class SecretBulkImportView(BulkImportView):
class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'secrets.change_secret'
cls = Secret
queryset = Secret.objects.select_related('role', 'device')
filter = filters.SecretFilter
table = tables.SecretTable
form = forms.SecretBulkEditForm
@ -248,5 +251,7 @@ class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'secrets.delete_secret'
cls = Secret
queryset = Secret.objects.select_related('role', 'device')
filter = filters.SecretFilter
table = tables.SecretTable
default_return_url = 'secrets:secret_list'

View File

@ -42,6 +42,8 @@ class TenantGroupEditView(TenantGroupCreateView):
class TenantGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'tenancy.delete_tenantgroup'
cls = TenantGroup
queryset = TenantGroup.objects.annotate(tenant_count=Count('tenants'))
table = tables.TenantGroupTable
default_return_url = 'tenancy:tenantgroup_list'
@ -113,6 +115,7 @@ class TenantBulkImportView(PermissionRequiredMixin, BulkImportView):
class TenantBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'tenancy.change_tenant'
cls = Tenant
queryset = Tenant.objects.select_related('group')
filter = filters.TenantFilter
table = tables.TenantTable
form = forms.TenantBulkEditForm
@ -122,5 +125,7 @@ class TenantBulkEditView(PermissionRequiredMixin, BulkEditView):
class TenantBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'tenancy.delete_tenant'
cls = Tenant
queryset = Tenant.objects.select_related('group')
filter = filters.TenantFilter
table = tables.TenantTable
default_return_url = 'tenancy:tenant_list'

View File

@ -461,6 +461,7 @@ class BulkEditView(View):
cls: The model of the objects being edited
parent_cls: The model of the parent object (if any)
queryset: Custom queryset to use when retrieving objects (e.g. to select related objects)
filter: FilterSet to apply when deleting by QuerySet
table: The table used to display devices being edited
form: The form class used to edit objects in bulk
@ -470,9 +471,10 @@ class BulkEditView(View):
"""
cls = None
parent_cls = None
queryset = None
filter = None
form = None
table = None
form = None
template_name = 'utilities/obj_bulk_edit.html'
default_return_url = 'home'
@ -539,7 +541,9 @@ class BulkEditView(View):
initial_data['pk'] = pk_list
form = self.form(self.cls, initial=initial_data)
table = self.table(self.cls.objects.filter(pk__in=pk_list), orderable=False)
# Retrieve objects being edited
queryset = self.queryset or self.cls.objects.all()
table = self.table(queryset.filter(pk__in=pk_list), orderable=False)
if not table.rows:
messages.warning(request, "No {} were selected.".format(self.cls._meta.verbose_name_plural))
return redirect(return_url)
@ -605,6 +609,7 @@ class BulkDeleteView(View):
cls: The model of the objects being deleted
parent_cls: The model of the parent object (if any)
queryset: Custom queryset to use when retrieving objects (e.g. to select related objects)
filter: FilterSet to apply when deleting by QuerySet
table: The table used to display devices being deleted
form: The form class used to delete objects in bulk
@ -614,6 +619,7 @@ class BulkDeleteView(View):
"""
cls = None
parent_cls = None
queryset = None
filter = None
table = None
form = None
@ -665,7 +671,9 @@ class BulkDeleteView(View):
else:
form = form_cls(initial={'pk': pk_list, 'return_url': return_url})
table = self.table(self.cls.objects.filter(pk__in=pk_list), orderable=False)
# Retrieve objects being deleted
queryset = self.queryset or self.cls.objects.all()
table = self.table(queryset.filter(pk__in=pk_list), orderable=False)
if not table.rows:
messages.warning(request, "No {} were selected for deletion.".format(self.cls._meta.verbose_name_plural))
return redirect(return_url)