Преглед изворни кода

Implement get_subquery() for annotation of child object counts; Rename dcim.Site 'count_*' fields

Jeremy Stretch пре 6 година
родитељ
комит
f4bbdf30e8
6 измењених фајлова са 50 додато и 58 уклоњено
  1. 2 0
      CHANGELOG.md
  2. 8 7
      netbox/dcim/api/serializers.py
  3. 19 17
      netbox/dcim/api/views.py
  4. 0 26
      netbox/dcim/models.py
  5. 3 8
      netbox/ipam/api/views.py
  6. 18 0
      netbox/utilities/utils.py

+ 2 - 0
CHANGELOG.md

@@ -155,6 +155,8 @@ functionality provided by the front end UI.
 * dcim.DeviceType: `instance_count` has been renamed to `device_count`.
 * dcim.Interface: `form_factor` has been renamed to `type`. Backward compatibility for `form_factor` will be maintained until NetBox v2.7.
 * dcim.Interface: The `type` filter has been renamed to `kind`.
+* dcim.Site: The `count_*` read-only fields have been renamed to `*_count` for consistency with other objects.
+* dcim.Site: Added the `virtualmachine_count` read-only field.
 * extras.Tag: Added `color` and `comments` fields to the Tag serializer.
 * virtualization.VirtualMachine: The virtual machines list endpoint now includes rendered context data.
 

+ 8 - 7
netbox/dcim/api/serializers.py

@@ -72,19 +72,20 @@ class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     time_zone = TimeZoneField(required=False)
     tags = TagListSerializerField(required=False)
-    count_prefixes = serializers.IntegerField(read_only=True)
-    count_vlans = serializers.IntegerField(read_only=True)
-    count_racks = serializers.IntegerField(read_only=True)
-    count_devices = serializers.IntegerField(read_only=True)
-    count_circuits = serializers.IntegerField(read_only=True)
+    prefix_count = serializers.IntegerField(read_only=True)
+    vlan_count = serializers.IntegerField(read_only=True)
+    rack_count = serializers.IntegerField(read_only=True)
+    device_count = serializers.IntegerField(read_only=True)
+    circuit_count = serializers.IntegerField(read_only=True)
+    virtualmachine_count = serializers.IntegerField(read_only=True)
 
     class Meta:
         model = Site
         fields = [
             'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
             'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
-            'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'count_prefixes',
-            'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
+            'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'prefix_count',
+            'vlan_count', 'rack_count', 'device_count', 'circuit_count', 'virtualmachine_count',
         ]
 
 

+ 19 - 17
netbox/dcim/api/views.py

@@ -12,6 +12,7 @@ from rest_framework.mixins import ListModelMixin
 from rest_framework.response import Response
 from rest_framework.viewsets import GenericViewSet, ViewSet
 
+from circuits.models import Circuit
 from dcim import filters
 from dcim.models import (
     Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
@@ -23,9 +24,11 @@ from dcim.models import (
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
+from ipam.models import Prefix, VLAN
 from utilities.api import (
     get_serializer_for_model, IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable,
 )
+from utilities.utils import get_subquery
 from virtualization.models import VirtualMachine
 from . import serializers
 from .exceptions import MissingFilterException
@@ -106,7 +109,18 @@ class RegionViewSet(ModelViewSet):
 #
 
 class SiteViewSet(CustomFieldModelViewSet):
-    queryset = Site.objects.select_related('region', 'tenant').prefetch_related('tags')
+    queryset = Site.objects.select_related(
+        'region', 'tenant'
+    ).prefetch_related(
+        'tags'
+    ).annotate(
+        device_count=get_subquery(Device, 'site'),
+        rack_count=get_subquery(Rack, 'site'),
+        prefix_count=get_subquery(Prefix, 'site'),
+        vlan_count=get_subquery(VLAN, 'site'),
+        circuit_count=get_subquery(Circuit, 'terminations__site'),
+        virtualmachine_count=get_subquery(VirtualMachine, 'cluster__site'),
+    )
     serializer_class = serializers.SiteSerializer
     filterset_class = filters.SiteFilter
 
@@ -281,15 +295,9 @@ class DeviceBayTemplateViewSet(ModelViewSet):
 #
 
 class DeviceRoleViewSet(ModelViewSet):
-    device_count = Device.objects.filter(
-        device_role=OuterRef('pk')
-    ).order_by().values('device_role').annotate(c=Count('*')).values('c')
-    virtualmachine_count = VirtualMachine.objects.filter(
-        role=OuterRef('pk')
-    ).order_by().values('role').annotate(c=Count('*')).values('c')
     queryset = DeviceRole.objects.annotate(
-        device_count=Subquery(device_count),
-        virtualmachine_count=Subquery(virtualmachine_count)
+        device_count=get_subquery(Device, 'device_role'),
+        virtualmachine_count=get_subquery(VirtualMachine, 'role')
     )
     serializer_class = serializers.DeviceRoleSerializer
     filterset_class = filters.DeviceRoleFilter
@@ -300,15 +308,9 @@ class DeviceRoleViewSet(ModelViewSet):
 #
 
 class PlatformViewSet(ModelViewSet):
-    device_count = Device.objects.filter(
-        platform=OuterRef('pk')
-    ).order_by().values('platform').annotate(c=Count('*')).values('c')
-    virtualmachine_count = VirtualMachine.objects.filter(
-        platform=OuterRef('pk')
-    ).order_by().values('platform').annotate(c=Count('*')).values('c')
     queryset = Platform.objects.annotate(
-        device_count=Subquery(device_count),
-        virtualmachine_count=Subquery(virtualmachine_count)
+        device_count=get_subquery(Device, 'platform'),
+        virtualmachine_count=get_subquery(VirtualMachine, 'platform')
     )
     serializer_class = serializers.PlatformSerializer
     filterset_class = filters.PlatformFilter

+ 0 - 26
netbox/dcim/models.py

@@ -363,32 +363,6 @@ class Site(ChangeLoggedModel, CustomFieldModel):
     def get_status_class(self):
         return STATUS_CLASSES[self.status]
 
-    @property
-    def count_prefixes(self):
-        return self.prefixes.count()
-
-    @property
-    def count_vlans(self):
-        return self.vlans.count()
-
-    @property
-    def count_racks(self):
-        return Rack.objects.filter(site=self).count()
-
-    @property
-    def count_devices(self):
-        return Device.objects.filter(site=self).count()
-
-    @property
-    def count_circuits(self):
-        from circuits.models import Circuit
-        return Circuit.objects.filter(terminations__site=self).count()
-
-    @property
-    def count_vms(self):
-        from virtualization.models import VirtualMachine
-        return VirtualMachine.objects.filter(cluster__site=self).count()
-
 
 #
 # Racks

+ 3 - 8
netbox/ipam/api/views.py

@@ -10,6 +10,7 @@ from extras.api.views import CustomFieldModelViewSet
 from ipam import filters
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from utilities.api import FieldChoicesViewSet, ModelViewSet
+from utilities.utils import get_subquery
 from . import serializers
 
 
@@ -66,15 +67,9 @@ class AggregateViewSet(CustomFieldModelViewSet):
 #
 
 class RoleViewSet(ModelViewSet):
-    prefix_count = Prefix.objects.filter(
-        role=OuterRef('pk')
-    ).order_by().values('role').annotate(c=Count('*')).values('c')
-    vlan_count = VLAN.objects.filter(
-        role=OuterRef('pk')
-    ).order_by().values('role').annotate(c=Count('*')).values('c')
     queryset = Role.objects.annotate(
-        prefix_count=Subquery(prefix_count),
-        vlan_count=Subquery(vlan_count)
+        prefix_count=get_subquery(Prefix, 'role'),
+        vlan_count=get_subquery(VLAN, 'role')
     )
     serializer_class = serializers.RoleSerializer
     filterset_class = filters.RoleFilter

+ 18 - 0
netbox/utilities/utils.py

@@ -4,6 +4,7 @@ import datetime
 import json
 
 from django.core.serializers import serialize
+from django.db.models import Count, OuterRef, Subquery
 
 from dcim.constants import LENGTH_UNIT_CENTIMETER, LENGTH_UNIT_FOOT, LENGTH_UNIT_INCH, LENGTH_UNIT_METER
 
@@ -71,6 +72,23 @@ def model_names_to_filter_dict(names):
     }
 
 
+def get_subquery(model, field):
+    """
+    Return a Subquery suitable for annotating a child object count.
+    """
+    subquery = Subquery(
+        model.objects.filter(
+            **{field: OuterRef('pk')}
+        ).order_by().values(
+            field
+        ).annotate(
+            c=Count('*')
+        ).values('c')
+    )
+
+    return subquery
+
+
 def serialize_object(obj, extra=None):
     """
     Return a generic JSON representation of an object using Django's built-in serializer. (This is used for things like