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

Closes #10197: Add a cached counter field for virtual chassis members

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

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

@@ -1156,13 +1156,15 @@ class CablePathSerializer(serializers.ModelSerializer):
 class VirtualChassisSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
     master = NestedDeviceSerializer(required=False, allow_null=True, default=None)
+
+    # Counter fields
     member_count = serializers.IntegerField(read_only=True)
 
     class Meta:
         model = VirtualChassis
         fields = [
             'id', 'url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags', 'custom_fields',
-            'member_count', 'created', 'last_updated',
+            'created', 'last_updated', 'member_count',
         ]
 
 

+ 1 - 3
netbox/dcim/api/views.py

@@ -579,9 +579,7 @@ class CableTerminationViewSet(NetBoxModelViewSet):
 #
 
 class VirtualChassisViewSet(NetBoxModelViewSet):
-    queryset = VirtualChassis.objects.prefetch_related('tags').annotate(
-        member_count=count_related(Device, 'virtual_chassis')
-    )
+    queryset = VirtualChassis.objects.prefetch_related('tags')
     serializer_class = serializers.VirtualChassisSerializer
     filterset_class = filtersets.VirtualChassisFilterSet
     brief_prefetch_fields = ['master']

+ 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
+        from .models import CableTermination, Device, VirtualChassis
         from utilities.counters import connect_counters
 
         # Register denormalized fields
@@ -27,4 +27,4 @@ class DCIMConfig(AppConfig):
         })
 
         # Register counters
-        connect_counters(Device)
+        connect_counters(Device, VirtualChassis)

+ 35 - 0
netbox/dcim/migrations/0177_virtual_chassis_member_counter.py

@@ -0,0 +1,35 @@
+from django.db import migrations
+from django.db.models import Count
+
+import utilities.fields
+
+
+def populate_virtualchassis_members(apps, schema_editor):
+    VirtualChassis = apps.get_model('dcim', 'VirtualChassis')
+
+    vcs = list(VirtualChassis.objects.annotate(_member_count=Count('members', distinct=True)))
+
+    for vc in vcs:
+        vc.member_count = vc._member_count
+
+    VirtualChassis.objects.bulk_update(vcs, ['member_count'])
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0176_device_component_counters'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='virtualchassis',
+            name='member_count',
+            field=utilities.fields.CounterCacheField(
+                default=0, to_field='virtual_chassis', to_model='dcim.Device'
+            ),
+        ),
+        migrations.RunPython(
+            code=populate_virtualchassis_members,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 8 - 1
netbox/dcim/models/devices.py

@@ -22,6 +22,7 @@ from netbox.config import ConfigItem
 from netbox.models import OrganizationalModel, PrimaryModel
 from utilities.choices import ColorChoices
 from utilities.fields import ColorField, CounterCacheField, NaturalOrderingField
+from utilities.tracking import TrackingModelMixin
 from .device_components import *
 from .mixins import WeightMixin
 
@@ -469,7 +470,7 @@ def update_interface_bridges(device, interface_templates, module=None):
             interface.save()
 
 
-class Device(PrimaryModel, ConfigContextModel):
+class Device(PrimaryModel, ConfigContextModel, TrackingModelMixin):
     """
     A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType,
     DeviceRole, and (optionally) a Platform. Device names are not required, however if one is set it must be unique.
@@ -1206,6 +1207,12 @@ class VirtualChassis(PrimaryModel):
         blank=True
     )
 
+    # Counter fields
+    member_count = CounterCacheField(
+        to_model='dcim.Device',
+        to_field='virtual_chassis'
+    )
+
     class Meta:
         ordering = ['name']
         verbose_name_plural = 'virtual chassis'

+ 1 - 3
netbox/dcim/views.py

@@ -3227,9 +3227,7 @@ class InterfaceConnectionsListView(generic.ObjectListView):
 #
 
 class VirtualChassisListView(generic.ObjectListView):
-    queryset = VirtualChassis.objects.annotate(
-        member_count=count_related(Device, 'virtual_chassis')
-    )
+    queryset = VirtualChassis.objects.all()
     table = tables.VirtualChassisTable
     filterset = filtersets.VirtualChassisFilterSet
     filterset_form = forms.VirtualChassisFilterForm

+ 10 - 0
netbox/templates/dcim/virtualchassis.html

@@ -31,6 +31,16 @@
             <th scope="row">Description</th>
             <td>{{ object.description|placeholder }}</td>
           </tr>
+          <tr>
+            <th scope="row">Members</th>
+            <td>
+              {% if object.member_count %}
+                <a href="{% url 'dcim:device_list' %}?virtual_chassis_id={{ object.pk }}">{{ object.member_count }}</a>
+              {% else %}
+                {{ object.member_count }}
+              {% endif %}
+            </td>
+          </tr>
         </table>
       </div>
     </div>