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

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

Jeremy Stretch 6 лет назад
Родитель
Сommit
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.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: `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.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.
 * extras.Tag: Added `color` and `comments` fields to the Tag serializer.
 * virtualization.VirtualMachine: The virtual machines list endpoint now includes rendered context data.
 * 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)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     time_zone = TimeZoneField(required=False)
     time_zone = TimeZoneField(required=False)
     tags = TagListSerializerField(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:
     class Meta:
         model = Site
         model = Site
         fields = [
         fields = [
             'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
             'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
             'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
             '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.response import Response
 from rest_framework.viewsets import GenericViewSet, ViewSet
 from rest_framework.viewsets import GenericViewSet, ViewSet
 
 
+from circuits.models import Circuit
 from dcim import filters
 from dcim import filters
 from dcim.models import (
 from dcim.models import (
     Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
     Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
@@ -23,9 +24,11 @@ from dcim.models import (
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.api.views import CustomFieldModelViewSet
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
+from ipam.models import Prefix, VLAN
 from utilities.api import (
 from utilities.api import (
     get_serializer_for_model, IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable,
     get_serializer_for_model, IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable,
 )
 )
+from utilities.utils import get_subquery
 from virtualization.models import VirtualMachine
 from virtualization.models import VirtualMachine
 from . import serializers
 from . import serializers
 from .exceptions import MissingFilterException
 from .exceptions import MissingFilterException
@@ -106,7 +109,18 @@ class RegionViewSet(ModelViewSet):
 #
 #
 
 
 class SiteViewSet(CustomFieldModelViewSet):
 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
     serializer_class = serializers.SiteSerializer
     filterset_class = filters.SiteFilter
     filterset_class = filters.SiteFilter
 
 
@@ -281,15 +295,9 @@ class DeviceBayTemplateViewSet(ModelViewSet):
 #
 #
 
 
 class DeviceRoleViewSet(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(
     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
     serializer_class = serializers.DeviceRoleSerializer
     filterset_class = filters.DeviceRoleFilter
     filterset_class = filters.DeviceRoleFilter
@@ -300,15 +308,9 @@ class DeviceRoleViewSet(ModelViewSet):
 #
 #
 
 
 class PlatformViewSet(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(
     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
     serializer_class = serializers.PlatformSerializer
     filterset_class = filters.PlatformFilter
     filterset_class = filters.PlatformFilter

+ 0 - 26
netbox/dcim/models.py

@@ -363,32 +363,6 @@ class Site(ChangeLoggedModel, CustomFieldModel):
     def get_status_class(self):
     def get_status_class(self):
         return STATUS_CLASSES[self.status]
         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
 # Racks

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

@@ -10,6 +10,7 @@ from extras.api.views import CustomFieldModelViewSet
 from ipam import filters
 from ipam import filters
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from utilities.api import FieldChoicesViewSet, ModelViewSet
 from utilities.api import FieldChoicesViewSet, ModelViewSet
+from utilities.utils import get_subquery
 from . import serializers
 from . import serializers
 
 
 
 
@@ -66,15 +67,9 @@ class AggregateViewSet(CustomFieldModelViewSet):
 #
 #
 
 
 class RoleViewSet(ModelViewSet):
 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(
     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
     serializer_class = serializers.RoleSerializer
     filterset_class = filters.RoleFilter
     filterset_class = filters.RoleFilter

+ 18 - 0
netbox/utilities/utils.py

@@ -4,6 +4,7 @@ import datetime
 import json
 import json
 
 
 from django.core.serializers import serialize
 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
 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):
 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
     Return a generic JSON representation of an object using Django's built-in serializer. (This is used for things like