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