Просмотр исходного кода

Closes #13269: Cache component template counts on device types

Jeremy Stretch 2 лет назад
Родитель
Сommit
5b5444f414

+ 19 - 3
netbox/dcim/api/serializers.py

@@ -327,12 +327,28 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
     weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
     device_count = serializers.IntegerField(read_only=True)
 
+    # Counter fields
+    console_port_template_count = serializers.IntegerField(read_only=True)
+    console_server_port_template_count = serializers.IntegerField(read_only=True)
+    power_port_template_count = serializers.IntegerField(read_only=True)
+    power_outlet_template_count = serializers.IntegerField(read_only=True)
+    interface_template_count = serializers.IntegerField(read_only=True)
+    front_port_template_count = serializers.IntegerField(read_only=True)
+    rear_port_template_count = serializers.IntegerField(read_only=True)
+    device_bay_template_count = serializers.IntegerField(read_only=True)
+    module_bay_template_count = serializers.IntegerField(read_only=True)
+    inventory_item_template_count = serializers.IntegerField(read_only=True)
+
     class Meta:
         model = DeviceType
         fields = [
-            'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
-            'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', 'description',
-            'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
+            'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height',
+            'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image',
+            'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
+            'console_port_template_count', 'console_server_port_template_count', 'power_port_template_count',
+            'power_outlet_template_count', 'interface_template_count', 'front_port_template_count',
+            'rear_port_template_count', 'device_bay_template_count', 'module_bay_template_count',
+            'inventory_item_template_count',
         ]
 
 

+ 2 - 2
netbox/dcim/apps.py

@@ -9,7 +9,7 @@ class DCIMConfig(AppConfig):
 
     def ready(self):
         from . import signals, search
-        from .models import CableTermination, Device, VirtualChassis
+        from .models import CableTermination, Device, DeviceType, VirtualChassis
         from utilities.counters import connect_counters
 
         # Register denormalized fields
@@ -27,4 +27,4 @@ class DCIMConfig(AppConfig):
         })
 
         # Register counters
-        connect_counters(Device, VirtualChassis)
+        connect_counters(Device, DeviceType, VirtualChassis)

+ 10 - 2
netbox/dcim/migrations/0176_device_component_counters.py

@@ -32,8 +32,16 @@ def recalculate_device_counts(apps, schema_editor):
         device.inventory_item_count = device._inventory_item_count
 
     Device.objects.bulk_update(devices, [
-        'console_port_count', 'console_server_port_count', 'power_port_count', 'power_outlet_count', 'interface_count',
-        'front_port_count', 'rear_port_count', 'device_bay_count', 'module_bay_count', 'inventory_item_count',
+        'console_port_count',
+        'console_server_port_count',
+        'power_port_count',
+        'power_outlet_count',
+        'interface_count',
+        'front_port_count',
+        'rear_port_count',
+        'device_bay_count',
+        'module_bay_count',
+        'inventory_item_count',
     ])
 
 

+ 108 - 0
netbox/dcim/migrations/0177_devicetype_component_counters.py

@@ -0,0 +1,108 @@
+from django.db import migrations
+from django.db.models import Count
+
+import utilities.fields
+
+
+def recalculate_devicetype_template_counts(apps, schema_editor):
+    DeviceType = apps.get_model("dcim", "DeviceType")
+    device_types = list(DeviceType.objects.all().annotate(
+        _console_port_template_count=Count('consoleporttemplates', distinct=True),
+        _console_server_port_template_count=Count('consoleserverporttemplates', distinct=True),
+        _power_port_template_count=Count('powerporttemplates', distinct=True),
+        _power_outlet_template_count=Count('poweroutlettemplates', distinct=True),
+        _interface_template_count=Count('interfacetemplates', distinct=True),
+        _front_port_template_count=Count('frontporttemplates', distinct=True),
+        _rear_port_template_count=Count('rearporttemplates', distinct=True),
+        _device_bay_template_count=Count('devicebaytemplates', distinct=True),
+        _module_bay_template_count=Count('modulebaytemplates', distinct=True),
+        _inventory_item_template_count=Count('inventoryitemtemplates', distinct=True),
+    ))
+
+    for devicetype in device_types:
+        devicetype.console_port_template_count = devicetype._console_port_template_count
+        devicetype.console_server_port_template_count = devicetype._console_server_port_template_count
+        devicetype.power_port_template_count = devicetype._power_port_template_count
+        devicetype.power_outlet_template_count = devicetype._power_outlet_template_count
+        devicetype.interface_template_count = devicetype._interface_template_count
+        devicetype.front_port_template_count = devicetype._front_port_template_count
+        devicetype.rear_port_template_count = devicetype._rear_port_template_count
+        devicetype.device_bay_template_count = devicetype._device_bay_template_count
+        devicetype.module_bay_template_count = devicetype._module_bay_template_count
+        devicetype.inventory_item_template_count = devicetype._inventory_item_template_count
+
+    DeviceType.objects.bulk_update(device_types, [
+        'console_port_template_count',
+        'console_server_port_template_count',
+        'power_port_template_count',
+        'power_outlet_template_count',
+        'interface_template_count',
+        'front_port_template_count',
+        'rear_port_template_count',
+        'device_bay_template_count',
+        'module_bay_template_count',
+        'inventory_item_template_count',
+    ])
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0176_device_component_counters'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='devicetype',
+            name='console_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ConsolePortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='console_server_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ConsoleServerPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='power_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.PowerPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='power_outlet_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.PowerOutletTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='interface_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.InterfaceTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='front_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.FrontPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='rear_port_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.RearPortTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='device_bay_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.DeviceBayTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='module_bay_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.ModuleBayTemplate'),
+        ),
+        migrations.AddField(
+            model_name='devicetype',
+            name='inventory_item_template_count',
+            field=utilities.fields.CounterCacheField(default=0, to_field='device_type', to_model='dcim.InventoryItemTemplate'),
+        ),
+        migrations.RunPython(
+            recalculate_devicetype_template_counts,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 1 - 1
netbox/dcim/migrations/0177_virtual_chassis_member_counter.py → netbox/dcim/migrations/0178_virtual_chassis_member_counter.py

@@ -17,7 +17,7 @@ def populate_virtualchassis_members(apps, schema_editor):
 
 class Migration(migrations.Migration):
     dependencies = [
-        ('dcim', '0176_device_component_counters'),
+        ('dcim', '0177_devicetype_component_counters'),
     ]
 
     operations = [

+ 2 - 1
netbox/dcim/models/device_component_templates.py

@@ -12,6 +12,7 @@ from netbox.models import ChangeLoggedModel
 from utilities.fields import ColorField, NaturalOrderingField
 from utilities.mptt import TreeManager
 from utilities.ordering import naturalize_interface
+from utilities.tracking import TrackingModelMixin
 from .device_components import (
     ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort,
     RearPort,
@@ -32,7 +33,7 @@ __all__ = (
 )
 
 
-class ComponentTemplateModel(ChangeLoggedModel):
+class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
     device_type = models.ForeignKey(
         to='dcim.DeviceType',
         on_delete=models.CASCADE,

+ 42 - 0
netbox/dcim/models/devices.py

@@ -129,6 +129,48 @@ class DeviceType(PrimaryModel, WeightMixin):
         blank=True
     )
 
+    # Counter fields
+    console_port_template_count = CounterCacheField(
+        to_model='dcim.ConsolePortTemplate',
+        to_field='device_type'
+    )
+    console_server_port_template_count = CounterCacheField(
+        to_model='dcim.ConsoleServerPortTemplate',
+        to_field='device_type'
+    )
+    power_port_template_count = CounterCacheField(
+        to_model='dcim.PowerPortTemplate',
+        to_field='device_type'
+    )
+    power_outlet_template_count = CounterCacheField(
+        to_model='dcim.PowerOutletTemplate',
+        to_field='device_type'
+    )
+    interface_template_count = CounterCacheField(
+        to_model='dcim.InterfaceTemplate',
+        to_field='device_type'
+    )
+    front_port_template_count = CounterCacheField(
+        to_model='dcim.FrontPortTemplate',
+        to_field='device_type'
+    )
+    rear_port_template_count = CounterCacheField(
+        to_model='dcim.RearPortTemplate',
+        to_field='device_type'
+    )
+    device_bay_template_count = CounterCacheField(
+        to_model='dcim.DeviceBayTemplate',
+        to_field='device_type'
+    )
+    module_bay_template_count = CounterCacheField(
+        to_model='dcim.ModuleBayTemplate',
+        to_field='device_type'
+    )
+    inventory_item_template_count = CounterCacheField(
+        to_model='dcim.InventoryItemTemplate',
+        to_field='device_type'
+    )
+
     images = GenericRelation(
         to='extras.ImageAttachment'
     )

+ 39 - 7
netbox/dcim/tables/devicetypes.py

@@ -1,4 +1,5 @@
 import django_tables2 as tables
+from django.utils.translation import gettext as _
 
 from dcim import models
 from netbox.tables import NetBoxTable, columns
@@ -83,11 +84,6 @@ class DeviceTypeTable(NetBoxTable):
     is_full_depth = columns.BooleanColumn(
         verbose_name='Full Depth'
     )
-    instance_count = columns.LinkedCountColumn(
-        viewname='dcim:device_list',
-        url_params={'device_type_id': 'pk'},
-        verbose_name='Instances'
-    )
     comments = columns.MarkdownColumn()
     tags = columns.TagColumn(
         url_name='dcim:devicetype_list'
@@ -99,12 +95,48 @@ class DeviceTypeTable(NetBoxTable):
         template_code=WEIGHT,
         order_by=('_abs_weight', 'weight_unit')
     )
+    instance_count = columns.LinkedCountColumn(
+        viewname='dcim:device_list',
+        url_params={'device_type_id': 'pk'},
+        verbose_name='Instances'
+    )
+    console_port_template_count = tables.Column(
+        verbose_name=_('Console ports')
+    )
+    console_server_port_template_count = tables.Column(
+        verbose_name=_('Console server ports')
+    )
+    power_port_template_count = tables.Column(
+        verbose_name=_('Power ports')
+    )
+    power_outlet_template_count = tables.Column(
+        verbose_name=_('Power outlets')
+    )
+    interface_template_count = tables.Column(
+        verbose_name=_('Interfaces')
+    )
+    front_port_template_count = tables.Column(
+        verbose_name=_('Front ports')
+    )
+    rear_port_template_count = tables.Column(
+        verbose_name=_('Rear ports')
+    )
+    device_bay_template_count = tables.Column(
+        verbose_name=_('Device bays')
+    )
+    module_bay_template_count = tables.Column(
+        verbose_name=_('Module bays')
+    )
+    inventory_item_template_count = tables.Column(
+        verbose_name=_('Inventory items')
+    )
 
     class Meta(NetBoxTable.Meta):
         model = models.DeviceType
         fields = (
-            'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
-            'airflow', 'weight', 'description', 'comments', 'instance_count', 'tags', 'created', 'last_updated',
+            'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height', 'is_full_depth',
+            'subdevice_role', 'airflow', 'weight', 'description', 'comments', 'instance_count', 'tags', 'created',
+            'last_updated',
         )
         default_columns = (
             'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',

+ 10 - 10
netbox/dcim/views.py

@@ -951,7 +951,7 @@ class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_consoleports'
     tab = ViewTab(
         label=_('Console Ports'),
-        badge=lambda obj: obj.consoleporttemplates.count(),
+        badge=lambda obj: obj.console_port_template_count,
         permission='dcim.view_consoleporttemplate',
         weight=550,
         hide_if_empty=True
@@ -966,7 +966,7 @@ class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_consoleserverports'
     tab = ViewTab(
         label=_('Console Server Ports'),
-        badge=lambda obj: obj.consoleserverporttemplates.count(),
+        badge=lambda obj: obj.console_server_port_template_count,
         permission='dcim.view_consoleserverporttemplate',
         weight=560,
         hide_if_empty=True
@@ -981,7 +981,7 @@ class DeviceTypePowerPortsView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_powerports'
     tab = ViewTab(
         label=_('Power Ports'),
-        badge=lambda obj: obj.powerporttemplates.count(),
+        badge=lambda obj: obj.power_port_template_count,
         permission='dcim.view_powerporttemplate',
         weight=570,
         hide_if_empty=True
@@ -996,7 +996,7 @@ class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_poweroutlets'
     tab = ViewTab(
         label=_('Power Outlets'),
-        badge=lambda obj: obj.poweroutlettemplates.count(),
+        badge=lambda obj: obj.power_outlet_template_count,
         permission='dcim.view_poweroutlettemplate',
         weight=580,
         hide_if_empty=True
@@ -1011,7 +1011,7 @@ class DeviceTypeInterfacesView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_interfaces'
     tab = ViewTab(
         label=_('Interfaces'),
-        badge=lambda obj: obj.interfacetemplates.count(),
+        badge=lambda obj: obj.interface_template_count,
         permission='dcim.view_interfacetemplate',
         weight=520,
         hide_if_empty=True
@@ -1026,7 +1026,7 @@ class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_frontports'
     tab = ViewTab(
         label=_('Front Ports'),
-        badge=lambda obj: obj.frontporttemplates.count(),
+        badge=lambda obj: obj.front_port_template_count,
         permission='dcim.view_frontporttemplate',
         weight=530,
         hide_if_empty=True
@@ -1041,7 +1041,7 @@ class DeviceTypeRearPortsView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_rearports'
     tab = ViewTab(
         label=_('Rear Ports'),
-        badge=lambda obj: obj.rearporttemplates.count(),
+        badge=lambda obj: obj.rear_port_template_count,
         permission='dcim.view_rearporttemplate',
         weight=540,
         hide_if_empty=True
@@ -1056,7 +1056,7 @@ class DeviceTypeModuleBaysView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_modulebays'
     tab = ViewTab(
         label=_('Module Bays'),
-        badge=lambda obj: obj.modulebaytemplates.count(),
+        badge=lambda obj: obj.module_bay_template_count,
         permission='dcim.view_modulebaytemplate',
         weight=510,
         hide_if_empty=True
@@ -1071,7 +1071,7 @@ class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_devicebays'
     tab = ViewTab(
         label=_('Device Bays'),
-        badge=lambda obj: obj.devicebaytemplates.count(),
+        badge=lambda obj: obj.device_bay_template_count,
         permission='dcim.view_devicebaytemplate',
         weight=500,
         hide_if_empty=True
@@ -1086,7 +1086,7 @@ class DeviceTypeInventoryItemsView(DeviceTypeComponentsView):
     viewname = 'dcim:devicetype_inventoryitems'
     tab = ViewTab(
         label=_('Inventory Items'),
-        badge=lambda obj: obj.inventoryitemtemplates.count(),
+        badge=lambda obj: obj.inventory_item_template_count,
         permission='dcim.view_invenotryitemtemplate',
         weight=590,
         hide_if_empty=True