From a260019a7fcb41b2ae162051d45ab1e9e14cfb27 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 10 Jul 2020 15:38:54 -0400 Subject: [PATCH] #4843: Use subqueries when counting multiple types of related objects --- netbox/dcim/tables.py | 34 ++++++++++----------------------- netbox/dcim/views.py | 18 +++++++++++------ netbox/ipam/tables.py | 10 +++------- netbox/ipam/views.py | 6 +++++- netbox/virtualization/tables.py | 18 +++++++++++------ netbox/virtualization/views.py | 6 +++++- 6 files changed, 47 insertions(+), 45 deletions(-) diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index d8cf41eaa..e7fd6ae11 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -103,20 +103,12 @@ DEVICEROLE_ACTIONS = """ {% endif %} """ -DEVICEROLE_DEVICE_COUNT = """ -{{ value }} +DEVICE_COUNT = """ +{{ value|default:0 }} """ -DEVICEROLE_VM_COUNT = """ -{{ value }} -""" - -PLATFORM_DEVICE_COUNT = """ -{{ value }} -""" - -PLATFORM_VM_COUNT = """ -{{ value }} +VM_COUNT = """ +{{ value|default:0 }} """ PLATFORM_ACTIONS = """ @@ -278,6 +270,7 @@ class RackGroupTable(BaseTable): class RackRoleTable(BaseTable): pk = ToggleColumn() + name = tables.Column(linkify=True) rack_count = tables.Column(verbose_name='Racks') color = tables.TemplateColumn(COLOR_LABEL) actions = tables.TemplateColumn( @@ -704,21 +697,18 @@ class DeviceBayTemplateTable(BaseTable): class DeviceRoleTable(BaseTable): pk = ToggleColumn() device_count = tables.TemplateColumn( - template_code=DEVICEROLE_DEVICE_COUNT, - accessor=Accessor('devices.count'), - orderable=False, + template_code=DEVICE_COUNT, verbose_name='Devices' ) vm_count = tables.TemplateColumn( - template_code=DEVICEROLE_VM_COUNT, - accessor=Accessor('virtual_machines.count'), - orderable=False, + template_code=VM_COUNT, verbose_name='VMs' ) color = tables.TemplateColumn( template_code=COLOR_LABEL, verbose_name='Label' ) + vm_role = BooleanColumn() actions = tables.TemplateColumn( template_code=DEVICEROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, @@ -738,15 +728,11 @@ class DeviceRoleTable(BaseTable): class PlatformTable(BaseTable): pk = ToggleColumn() device_count = tables.TemplateColumn( - template_code=PLATFORM_DEVICE_COUNT, - accessor=Accessor('devices.count'), - orderable=False, + template_code=DEVICE_COUNT, verbose_name='Devices' ) vm_count = tables.TemplateColumn( - template_code=PLATFORM_VM_COUNT, - accessor=Accessor('virtual_machines.count'), - orderable=False, + template_code=VM_COUNT, verbose_name='VMs' ) actions = tables.TemplateColumn( diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 68359fc05..748e41139 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -23,7 +23,7 @@ from ipam.models import Prefix, VLAN from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator -from utilities.utils import csv_format +from utilities.utils import csv_format, get_subquery from utilities.views import ( BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, GetReturnURLMixin, ObjectImportView, ObjectDeleteView, ObjectEditView, ObjectListView, @@ -557,9 +557,9 @@ class RackReservationBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class ManufacturerListView(PermissionRequiredMixin, ObjectListView): permission_required = 'dcim.view_manufacturer' queryset = Manufacturer.objects.annotate( - devicetype_count=Count('device_types', distinct=True), - inventoryitem_count=Count('inventory_items', distinct=True), - platform_count=Count('platforms', distinct=True), + devicetype_count=get_subquery(DeviceType, 'manufacturer'), + inventoryitem_count=get_subquery(InventoryItem, 'manufacturer'), + platform_count=get_subquery(Platform, 'manufacturer') ) table = tables.ManufacturerTable @@ -1020,7 +1020,10 @@ class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class DeviceRoleListView(PermissionRequiredMixin, ObjectListView): permission_required = 'dcim.view_devicerole' - queryset = DeviceRole.objects.all() + queryset = DeviceRole.objects.annotate( + device_count=get_subquery(Device, 'device_role'), + vm_count=get_subquery(VirtualMachine, 'role') + ) table = tables.DeviceRoleTable @@ -1055,7 +1058,10 @@ class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class PlatformListView(PermissionRequiredMixin, ObjectListView): permission_required = 'dcim.view_platform' - queryset = Platform.objects.all() + queryset = Platform.objects.annotate( + device_count=get_subquery(Device, 'device_role'), + vm_count=get_subquery(VirtualMachine, 'role') + ) table = tables.PlatformTable diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index ca48c2951..d624ba134 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -40,11 +40,11 @@ UTILIZATION_GRAPH = """ """ ROLE_PREFIX_COUNT = """ -{{ value }} +{{ value|default:0 }} """ ROLE_VLAN_COUNT = """ -{{ value }} +{{ value|default:0 }} """ ROLE_ACTIONS = """ @@ -319,15 +319,11 @@ class AggregateDetailTable(AggregateTable): class RoleTable(BaseTable): pk = ToggleColumn() prefix_count = tables.TemplateColumn( - accessor=Accessor('prefixes.count'), template_code=ROLE_PREFIX_COUNT, - orderable=False, verbose_name='Prefixes' ) vlan_count = tables.TemplateColumn( - accessor=Accessor('vlans.count'), template_code=ROLE_VLAN_COUNT, - orderable=False, verbose_name='VLANs' ) actions = tables.TemplateColumn( @@ -524,7 +520,7 @@ class InterfaceIPAddressTable(BaseTable): class VLANGroupTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn() + name = tables.Column(linkify=True) site = tables.LinkColumn( viewname='dcim:site', args=[Accessor('site.slug')] diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index e8041e8dd..fa81de77b 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -9,6 +9,7 @@ from django_tables2 import RequestConfig from dcim.models import Device, Interface from utilities.paginator import EnhancedPaginator +from utilities.utils import get_subquery from utilities.views import ( BulkCreateView, BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, ) @@ -407,7 +408,10 @@ class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class RoleListView(PermissionRequiredMixin, ObjectListView): permission_required = 'ipam.view_role' - queryset = Role.objects.all() + queryset = Role.objects.annotate( + prefix_count=get_subquery(Prefix, 'role'), + vlan_count=get_subquery(VLAN, 'role') + ) table = tables.RoleTable diff --git a/netbox/virtualization/tables.py b/netbox/virtualization/tables.py index d957e0053..6972637d0 100644 --- a/netbox/virtualization/tables.py +++ b/netbox/virtualization/tables.py @@ -34,6 +34,14 @@ VIRTUALMACHINE_PRIMARY_IP = """ {{ record.primary_ip4.address.ip|default:"" }} """ +CLUSTER_DEVICE_COUNT = """ +{{ value|default:0 }} +""" + +CLUSTER_VM_COUNT = """ +{{ value|default:0 }} +""" + # # Cluster types @@ -94,14 +102,12 @@ class ClusterTable(BaseTable): viewname='dcim:site', args=[Accessor('site.slug')] ) - device_count = tables.Column( - accessor=Accessor('devices.count'), - orderable=False, + device_count = tables.TemplateColumn( + template_code=CLUSTER_DEVICE_COUNT, verbose_name='Devices' ) - vm_count = tables.Column( - accessor=Accessor('virtual_machines.count'), - orderable=False, + vm_count = tables.TemplateColumn( + template_code=CLUSTER_VM_COUNT, verbose_name='VMs' ) tags = TagColumn( diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 0a05833f4..4da7ee313 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -10,6 +10,7 @@ from dcim.models import Device, Interface from dcim.tables import DeviceTable from extras.views import ObjectConfigContextView from ipam.models import Service +from utilities.utils import get_subquery from utilities.views import ( BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, ObjectDeleteView, ObjectEditView, ObjectListView, @@ -94,7 +95,10 @@ class ClusterGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class ClusterListView(PermissionRequiredMixin, ObjectListView): permission_required = 'virtualization.view_cluster' - queryset = Cluster.objects.prefetch_related('type', 'group', 'site', 'tenant') + queryset = Cluster.objects.prefetch_related('type', 'group', 'site', 'tenant').annotate( + device_count=get_subquery(Device, 'cluster'), + vm_count=get_subquery(VirtualMachine, 'cluster') + ) table = tables.ClusterTable filterset = filters.ClusterFilterSet filterset_form = forms.ClusterFilterForm