Skip to content

Commit 5b5444f

Browse files
committed
Closes #13269: Cache component template counts on device types
1 parent daa8f71 commit 5b5444f

9 files changed

+233
-26
lines changed

netbox/dcim/api/serializers.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,28 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
327327
weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
328328
device_count = serializers.IntegerField(read_only=True)
329329

330+
# Counter fields
331+
console_port_template_count = serializers.IntegerField(read_only=True)
332+
console_server_port_template_count = serializers.IntegerField(read_only=True)
333+
power_port_template_count = serializers.IntegerField(read_only=True)
334+
power_outlet_template_count = serializers.IntegerField(read_only=True)
335+
interface_template_count = serializers.IntegerField(read_only=True)
336+
front_port_template_count = serializers.IntegerField(read_only=True)
337+
rear_port_template_count = serializers.IntegerField(read_only=True)
338+
device_bay_template_count = serializers.IntegerField(read_only=True)
339+
module_bay_template_count = serializers.IntegerField(read_only=True)
340+
inventory_item_template_count = serializers.IntegerField(read_only=True)
341+
330342
class Meta:
331343
model = DeviceType
332344
fields = [
333-
'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
334-
'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', 'description',
335-
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
345+
'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height',
346+
'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image',
347+
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
348+
'console_port_template_count', 'console_server_port_template_count', 'power_port_template_count',
349+
'power_outlet_template_count', 'interface_template_count', 'front_port_template_count',
350+
'rear_port_template_count', 'device_bay_template_count', 'module_bay_template_count',
351+
'inventory_item_template_count',
336352
]
337353

338354

netbox/dcim/apps.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class DCIMConfig(AppConfig):
99

1010
def ready(self):
1111
from . import signals, search
12-
from .models import CableTermination, Device, VirtualChassis
12+
from .models import CableTermination, Device, DeviceType, VirtualChassis
1313
from utilities.counters import connect_counters
1414

1515
# Register denormalized fields
@@ -27,4 +27,4 @@ def ready(self):
2727
})
2828

2929
# Register counters
30-
connect_counters(Device, VirtualChassis)
30+
connect_counters(Device, DeviceType, VirtualChassis)

netbox/dcim/migrations/0176_device_component_counters.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,16 @@ def recalculate_device_counts(apps, schema_editor):
3232
device.inventory_item_count = device._inventory_item_count
3333

3434
Device.objects.bulk_update(devices, [
35-
'console_port_count', 'console_server_port_count', 'power_port_count', 'power_outlet_count', 'interface_count',
36-
'front_port_count', 'rear_port_count', 'device_bay_count', 'module_bay_count', 'inventory_item_count',
35+
'console_port_count',
36+
'console_server_port_count',
37+
'power_port_count',
38+
'power_outlet_count',
39+
'interface_count',
40+
'front_port_count',
41+
'rear_port_count',
42+
'device_bay_count',
43+
'module_bay_count',
44+
'inventory_item_count',
3745
])
3846

3947

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from django.db import migrations
2+
from django.db.models import Count
3+
4+
import utilities.fields
5+
6+
7+
def recalculate_devicetype_template_counts(apps, schema_editor):
8+
DeviceType = apps.get_model("dcim", "DeviceType")
9+
device_types = list(DeviceType.objects.all().annotate(
10+
_console_port_template_count=Count('consoleporttemplates', distinct=True),
11+
_console_server_port_template_count=Count('consoleserverporttemplates', distinct=True),
12+
_power_port_template_count=Count('powerporttemplates', distinct=True),
13+
_power_outlet_template_count=Count('poweroutlettemplates', distinct=True),
14+
_interface_template_count=Count('interfacetemplates', distinct=True),
15+
_front_port_template_count=Count('frontporttemplates', distinct=True),
16+
_rear_port_template_count=Count('rearporttemplates', distinct=True),
17+
_device_bay_template_count=Count('devicebaytemplates', distinct=True),
18+
_module_bay_template_count=Count('modulebaytemplates', distinct=True),
19+
_inventory_item_template_count=Count('inventoryitemtemplates', distinct=True),
20+
))
21+
22+
for devicetype in device_types:
23+
devicetype.console_port_template_count = devicetype._console_port_template_count
24+
devicetype.console_server_port_template_count = devicetype._console_server_port_template_count
25+
devicetype.power_port_template_count = devicetype._power_port_template_count
26+
devicetype.power_outlet_template_count = devicetype._power_outlet_template_count
27+
devicetype.interface_template_count = devicetype._interface_template_count
28+
devicetype.front_port_template_count = devicetype._front_port_template_count
29+
devicetype.rear_port_template_count = devicetype._rear_port_template_count
30+
devicetype.device_bay_template_count = devicetype._device_bay_template_count
31+
devicetype.module_bay_template_count = devicetype._module_bay_template_count
32+
devicetype.inventory_item_template_count = devicetype._inventory_item_template_count
33+
34+
DeviceType.objects.bulk_update(device_types, [
35+
'console_port_template_count',
36+
'console_server_port_template_count',
37+
'power_port_template_count',
38+
'power_outlet_template_count',
39+
'interface_template_count',
40+
'front_port_template_count',
41+
'rear_port_template_count',
42+
'device_bay_template_count',
43+
'module_bay_template_count',
44+
'inventory_item_template_count',
45+
])
46+
47+
48+
class Migration(migrations.Migration):
49+
dependencies = [
50+
('dcim', '0176_device_component_counters'),
51+
]
52+
53+
operations = [
54+
migrations.AddField(
55+
model_name='devicetype',
56+
name='console_port_template_count',
57+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ConsolePortTemplate'),
58+
),
59+
migrations.AddField(
60+
model_name='devicetype',
61+
name='console_server_port_template_count',
62+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ConsoleServerPortTemplate'),
63+
),
64+
migrations.AddField(
65+
model_name='devicetype',
66+
name='power_port_template_count',
67+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.PowerPortTemplate'),
68+
),
69+
migrations.AddField(
70+
model_name='devicetype',
71+
name='power_outlet_template_count',
72+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.PowerOutletTemplate'),
73+
),
74+
migrations.AddField(
75+
model_name='devicetype',
76+
name='interface_template_count',
77+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.InterfaceTemplate'),
78+
),
79+
migrations.AddField(
80+
model_name='devicetype',
81+
name='front_port_template_count',
82+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.FrontPortTemplate'),
83+
),
84+
migrations.AddField(
85+
model_name='devicetype',
86+
name='rear_port_template_count',
87+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.RearPortTemplate'),
88+
),
89+
migrations.AddField(
90+
model_name='devicetype',
91+
name='device_bay_template_count',
92+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.DeviceBayTemplate'),
93+
),
94+
migrations.AddField(
95+
model_name='devicetype',
96+
name='module_bay_template_count',
97+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ModuleBayTemplate'),
98+
),
99+
migrations.AddField(
100+
model_name='devicetype',
101+
name='inventory_item_template_count',
102+
field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.InventoryItemTemplate'),
103+
),
104+
migrations.RunPython(
105+
recalculate_devicetype_template_counts,
106+
reverse_code=migrations.RunPython.noop
107+
),
108+
]

netbox/dcim/migrations/0177_virtual_chassis_member_counter.py renamed to netbox/dcim/migrations/0178_virtual_chassis_member_counter.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def populate_virtualchassis_members(apps, schema_editor):
1717

1818
class Migration(migrations.Migration):
1919
dependencies = [
20-
('dcim', '0176_device_component_counters'),
20+
('dcim', '0177_devicetype_component_counters'),
2121
]
2222

2323
operations = [

netbox/dcim/models/device_component_templates.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from utilities.fields import ColorField, NaturalOrderingField
1313
from utilities.mptt import TreeManager
1414
from utilities.ordering import naturalize_interface
15+
from utilities.tracking import TrackingModelMixin
1516
from .device_components import (
1617
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort,
1718
RearPort,
@@ -32,7 +33,7 @@
3233
)
3334

3435

35-
class ComponentTemplateModel(ChangeLoggedModel):
36+
class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
3637
device_type = models.ForeignKey(
3738
to='dcim.DeviceType',
3839
on_delete=models.CASCADE,

netbox/dcim/models/devices.py

+42
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,48 @@ class DeviceType(PrimaryModel, WeightMixin):
129129
blank=True
130130
)
131131

132+
# Counter fields
133+
console_port_template_count = CounterCacheField(
134+
to_model='dcim.ConsolePortTemplate',
135+
to_field='device_type'
136+
)
137+
console_server_port_template_count = CounterCacheField(
138+
to_model='dcim.ConsoleServerPortTemplate',
139+
to_field='device_type'
140+
)
141+
power_port_template_count = CounterCacheField(
142+
to_model='dcim.PowerPortTemplate',
143+
to_field='device_type'
144+
)
145+
power_outlet_template_count = CounterCacheField(
146+
to_model='dcim.PowerOutletTemplate',
147+
to_field='device_type'
148+
)
149+
interface_template_count = CounterCacheField(
150+
to_model='dcim.InterfaceTemplate',
151+
to_field='device_type'
152+
)
153+
front_port_template_count = CounterCacheField(
154+
to_model='dcim.FrontPortTemplate',
155+
to_field='device_type'
156+
)
157+
rear_port_template_count = CounterCacheField(
158+
to_model='dcim.RearPortTemplate',
159+
to_field='device_type'
160+
)
161+
device_bay_template_count = CounterCacheField(
162+
to_model='dcim.DeviceBayTemplate',
163+
to_field='device_type'
164+
)
165+
module_bay_template_count = CounterCacheField(
166+
to_model='dcim.ModuleBayTemplate',
167+
to_field='device_type'
168+
)
169+
inventory_item_template_count = CounterCacheField(
170+
to_model='dcim.InventoryItemTemplate',
171+
to_field='device_type'
172+
)
173+
132174
images = GenericRelation(
133175
to='extras.ImageAttachment'
134176
)

netbox/dcim/tables/devicetypes.py

+39-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import django_tables2 as tables
2+
from django.utils.translation import gettext as _
23

34
from dcim import models
45
from netbox.tables import NetBoxTable, columns
@@ -83,11 +84,6 @@ class DeviceTypeTable(NetBoxTable):
8384
is_full_depth = columns.BooleanColumn(
8485
verbose_name='Full Depth'
8586
)
86-
instance_count = columns.LinkedCountColumn(
87-
viewname='dcim:device_list',
88-
url_params={'device_type_id': 'pk'},
89-
verbose_name='Instances'
90-
)
9187
comments = columns.MarkdownColumn()
9288
tags = columns.TagColumn(
9389
url_name='dcim:devicetype_list'
@@ -99,12 +95,48 @@ class DeviceTypeTable(NetBoxTable):
9995
template_code=WEIGHT,
10096
order_by=('_abs_weight', 'weight_unit')
10197
)
98+
instance_count = columns.LinkedCountColumn(
99+
viewname='dcim:device_list',
100+
url_params={'device_type_id': 'pk'},
101+
verbose_name='Instances'
102+
)
103+
console_port_template_count = tables.Column(
104+
verbose_name=_('Console ports')
105+
)
106+
console_server_port_template_count = tables.Column(
107+
verbose_name=_('Console server ports')
108+
)
109+
power_port_template_count = tables.Column(
110+
verbose_name=_('Power ports')
111+
)
112+
power_outlet_template_count = tables.Column(
113+
verbose_name=_('Power outlets')
114+
)
115+
interface_template_count = tables.Column(
116+
verbose_name=_('Interfaces')
117+
)
118+
front_port_template_count = tables.Column(
119+
verbose_name=_('Front ports')
120+
)
121+
rear_port_template_count = tables.Column(
122+
verbose_name=_('Rear ports')
123+
)
124+
device_bay_template_count = tables.Column(
125+
verbose_name=_('Device bays')
126+
)
127+
module_bay_template_count = tables.Column(
128+
verbose_name=_('Module bays')
129+
)
130+
inventory_item_template_count = tables.Column(
131+
verbose_name=_('Inventory items')
132+
)
102133

103134
class Meta(NetBoxTable.Meta):
104135
model = models.DeviceType
105136
fields = (
106-
'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
107-
'airflow', 'weight', 'description', 'comments', 'instance_count', 'tags', 'created', 'last_updated',
137+
'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height', 'is_full_depth',
138+
'subdevice_role', 'airflow', 'weight', 'description', 'comments', 'instance_count', 'tags', 'created',
139+
'last_updated',
108140
)
109141
default_columns = (
110142
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',

0 commit comments

Comments
 (0)