1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Added child counts to API serializers (WIP)

This commit is contained in:
Jeremy Stretch
2019-04-12 17:07:56 -04:00
parent b1c160f9d4
commit a46b43bff6
26 changed files with 206 additions and 94 deletions

View File

@ -63,6 +63,8 @@ to now use "Extras | Tag."
* dcim.Interface: `form_factor` has been renamed to `type`. Backward-compatibile support for `form_factor` will be maintained until NetBox v2.7.
* dcim.Interface: The `type` filter has been renamed to `kind`.
* dcim.DeviceType: `instance_count` has been renamed to `device_count`.
## Bug Fixes

View File

@ -17,10 +17,11 @@ __all__ = [
class NestedProviderSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = Provider
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'circuit_count']
#
@ -29,10 +30,11 @@ class NestedProviderSerializer(WritableNestedSerializer):
class NestedCircuitTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = CircuitType
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'circuit_count']
class NestedCircuitSerializer(WritableNestedSerializer):

View File

@ -1,3 +1,4 @@
from rest_framework import serializers
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
from circuits.constants import CIRCUIT_STATUS_CHOICES
@ -16,12 +17,13 @@ from .nested_serializers import *
class ProviderSerializer(TaggitSerializer, CustomFieldModelSerializer):
tags = TagListSerializerField(required=False)
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = Provider
fields = [
'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
'custom_fields', 'created', 'last_updated', 'circuit_count',
]
@ -30,10 +32,11 @@ class ProviderSerializer(TaggitSerializer, CustomFieldModelSerializer):
#
class CircuitTypeSerializer(ValidatedModelSerializer):
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = CircuitType
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'circuit_count']
class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):

View File

@ -1,3 +1,4 @@
from django.db.models import Count
from django.shortcuts import get_object_or_404
from rest_framework.decorators import action
from rest_framework.response import Response
@ -27,7 +28,9 @@ class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
#
class ProviderViewSet(CustomFieldModelViewSet):
queryset = Provider.objects.prefetch_related('tags')
queryset = Provider.objects.prefetch_related('tags').annotate(
circuit_count=Count('circuits')
)
serializer_class = serializers.ProviderSerializer
filterset_class = filters.ProviderFilter
@ -47,7 +50,9 @@ class ProviderViewSet(CustomFieldModelViewSet):
#
class CircuitTypeViewSet(ModelViewSet):
queryset = CircuitType.objects.all()
queryset = CircuitType.objects.annotate(
circuit_count=Count('circuits')
)
serializer_class = serializers.CircuitTypeSerializer
filterset_class = filters.CircuitTypeFilter

View File

@ -61,7 +61,7 @@ class ProviderTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['circuit_count', 'id', 'name', 'slug', 'url']
)
def test_create_provider(self):
@ -162,7 +162,7 @@ class CircuitTypeTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['circuit_count', 'id', 'name', 'slug', 'url']
)
def test_create_circuittype(self):

View File

@ -42,10 +42,11 @@ __all__ = [
class NestedRegionSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
site_count = serializers.IntegerField(read_only=True)
class Meta:
model = Region
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'site_count']
class NestedSiteSerializer(WritableNestedSerializer):
@ -62,26 +63,29 @@ class NestedSiteSerializer(WritableNestedSerializer):
class NestedRackGroupSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackGroup
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'rack_count']
class NestedRackRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackRole
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'rack_count']
class NestedRackSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = Rack
fields = ['id', 'url', 'name', 'display_name']
fields = ['id', 'url', 'name', 'display_name', 'device_count']
#
@ -90,19 +94,21 @@ class NestedRackSerializer(WritableNestedSerializer):
class NestedManufacturerSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
devicetype_count = serializers.IntegerField(read_only=True)
class Meta:
model = Manufacturer
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'devicetype_count']
class NestedDeviceTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
manufacturer = NestedManufacturerSerializer(read_only=True)
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceType
fields = ['id', 'url', 'manufacturer', 'model', 'slug', 'display_name']
fields = ['id', 'url', 'manufacturer', 'model', 'slug', 'display_name', 'device_count']
class NestedRearPortTemplateSerializer(WritableNestedSerializer):
@ -127,18 +133,22 @@ class NestedFrontPortTemplateSerializer(WritableNestedSerializer):
class NestedDeviceRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceRole
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'device_count', 'virtualmachine_count']
class NestedPlatformSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = Platform
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'device_count', 'virtualmachine_count']
class NestedDeviceSerializer(WritableNestedSerializer):
@ -245,10 +255,11 @@ class NestedCableSerializer(serializers.ModelSerializer):
class NestedVirtualChassisSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
master = NestedDeviceSerializer()
member_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualChassis
fields = ['id', 'url', 'master']
fields = ['id', 'url', 'master', 'member_count']
#
@ -257,10 +268,11 @@ class NestedVirtualChassisSerializer(WritableNestedSerializer):
class NestedPowerPanelSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
powerfeed_count = serializers.IntegerField(read_only=True)
class Meta:
model = PowerPanel
fields = ['id', 'url', 'name']
fields = ['id', 'url', 'name', 'powerfeed_count']
class NestedPowerFeedSerializer(WritableNestedSerializer):

View File

@ -59,10 +59,11 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer):
class RegionSerializer(serializers.ModelSerializer):
parent = NestedRegionSerializer(required=False, allow_null=True)
site_count = serializers.IntegerField(read_only=True)
class Meta:
model = Region
fields = ['id', 'name', 'slug', 'parent']
fields = ['id', 'name', 'slug', 'parent', 'site_count']
class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
@ -93,17 +94,19 @@ class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
class RackGroupSerializer(ValidatedModelSerializer):
site = NestedSiteSerializer()
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackGroup
fields = ['id', 'name', 'slug', 'site']
fields = ['id', 'name', 'slug', 'site', 'rack_count']
class RackRoleSerializer(ValidatedModelSerializer):
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackRole
fields = ['id', 'name', 'slug', 'color']
fields = ['id', 'name', 'slug', 'color', 'rack_count']
class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
@ -116,13 +119,14 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
outer_unit = ChoiceField(choices=RACK_DIMENSION_UNIT_CHOICES, required=False)
tags = TagListSerializerField(required=False)
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = Rack
fields = [
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
]
# Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
# prevents facility_id from being interpreted as a required field.
@ -169,23 +173,24 @@ class RackReservationSerializer(ValidatedModelSerializer):
#
class ManufacturerSerializer(ValidatedModelSerializer):
devicetype_count = serializers.IntegerField(read_only=True)
class Meta:
model = Manufacturer
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'devicetype_count']
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
manufacturer = NestedManufacturerSerializer()
subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
instance_count = serializers.IntegerField(source='instances.count', read_only=True)
tags = TagListSerializerField(required=False)
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceType
fields = [
'id', 'manufacturer', 'model', 'slug', 'display_name', 'part_number', 'u_height', 'is_full_depth',
'subdevice_role', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'instance_count',
'subdevice_role', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
]
@ -272,18 +277,25 @@ class DeviceBayTemplateSerializer(ValidatedModelSerializer):
#
class DeviceRoleSerializer(ValidatedModelSerializer):
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceRole
fields = ['id', 'name', 'slug', 'color', 'vm_role']
fields = ['id', 'name', 'slug', 'color', 'vm_role', 'device_count', 'virtualmachine_count']
class PlatformSerializer(ValidatedModelSerializer):
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = Platform
fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args']
fields = [
'id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'device_count',
'virtualmachine_count',
]
class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
@ -613,17 +625,17 @@ class InterfaceConnectionSerializer(ValidatedModelSerializer):
class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
master = NestedDeviceSerializer()
tags = TagListSerializerField(required=False)
member_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualChassis
fields = ['id', 'master', 'domain', 'tags']
fields = ['id', 'master', 'domain', 'tags', 'member_count']
#
# Power panels
#
class PowerPanelSerializer(ValidatedModelSerializer):
site = NestedSiteSerializer()
rack_group = NestedRackGroupSerializer(
@ -631,10 +643,11 @@ class PowerPanelSerializer(ValidatedModelSerializer):
allow_null=True,
default=None
)
powerfeed_count = serializers.IntegerField(read_only=True)
class Meta:
model = PowerPanel
fields = ['id', 'site', 'rack_group', 'name']
fields = ['id', 'site', 'rack_group', 'name', 'powerfeed_count']
class PowerFeedSerializer(TaggitSerializer, CustomFieldModelSerializer):

View File

@ -1,7 +1,7 @@
from collections import OrderedDict
from django.conf import settings
from django.db.models import F
from django.db.models import Count, F
from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404
from drf_yasg import openapi
@ -93,7 +93,9 @@ class CableTraceMixin(object):
#
class RegionViewSet(ModelViewSet):
queryset = Region.objects.all()
queryset = Region.objects.annotate(
site_count=Count('sites')
)
serializer_class = serializers.RegionSerializer
filterset_class = filters.RegionFilter
@ -123,7 +125,9 @@ class SiteViewSet(CustomFieldModelViewSet):
#
class RackGroupViewSet(ModelViewSet):
queryset = RackGroup.objects.select_related('site')
queryset = RackGroup.objects.select_related('site').annotate(
rack_count=Count('racks')
)
serializer_class = serializers.RackGroupSerializer
filterset_class = filters.RackGroupFilter
@ -133,7 +137,9 @@ class RackGroupViewSet(ModelViewSet):
#
class RackRoleViewSet(ModelViewSet):
queryset = RackRole.objects.all()
queryset = RackRole.objects.annotate(
rack_count=Count('racks')
)
serializer_class = serializers.RackRoleSerializer
filterset_class = filters.RackRoleFilter
@ -143,7 +149,13 @@ class RackRoleViewSet(ModelViewSet):
#
class RackViewSet(CustomFieldModelViewSet):
queryset = Rack.objects.select_related('site', 'group__site', 'tenant').prefetch_related('tags')
queryset = Rack.objects.select_related(
'site', 'group__site', 'tenant'
).prefetch_related(
'tags'
).annotate(
device_count=Count('devices')
)
serializer_class = serializers.RackSerializer
filterset_class = filters.RackFilter
@ -192,7 +204,9 @@ class RackReservationViewSet(ModelViewSet):
#
class ManufacturerViewSet(ModelViewSet):
queryset = Manufacturer.objects.all()
queryset = Manufacturer.objects.annotate(
devicetype_count=Count('device_types')
)
serializer_class = serializers.ManufacturerSerializer
filterset_class = filters.ManufacturerFilter
@ -202,7 +216,9 @@ class ManufacturerViewSet(ModelViewSet):
#
class DeviceTypeViewSet(CustomFieldModelViewSet):
queryset = DeviceType.objects.select_related('manufacturer').prefetch_related('tags')
queryset = DeviceType.objects.select_related('manufacturer').prefetch_related('tags').annotate(
device_count=Count('instances')
)
serializer_class = serializers.DeviceTypeSerializer
filterset_class = filters.DeviceTypeFilter
@ -264,7 +280,10 @@ class DeviceBayTemplateViewSet(ModelViewSet):
#
class DeviceRoleViewSet(ModelViewSet):
queryset = DeviceRole.objects.all()
queryset = DeviceRole.objects.annotate(
device_count=Count('devices', distinct=True),
virtualmachine_count=Count('virtual_machines', distinct=True)
)
serializer_class = serializers.DeviceRoleSerializer
filterset_class = filters.DeviceRoleFilter
@ -274,7 +293,10 @@ class DeviceRoleViewSet(ModelViewSet):
#
class PlatformViewSet(ModelViewSet):
queryset = Platform.objects.all()
queryset = Platform.objects.annotate(
device_count=Count('devices', distinct=True),
virtualmachine_count=Count('virtual_machines', distinct=True)
)
serializer_class = serializers.PlatformSerializer
filterset_class = filters.PlatformFilter
@ -535,7 +557,9 @@ class CableViewSet(ModelViewSet):
#
class VirtualChassisViewSet(ModelViewSet):
queryset = VirtualChassis.objects.prefetch_related('tags')
queryset = VirtualChassis.objects.prefetch_related('tags').annotate(
member_count=Count('members')
)
serializer_class = serializers.VirtualChassisSerializer
@ -544,7 +568,11 @@ class VirtualChassisViewSet(ModelViewSet):
#
class PowerPanelViewSet(ModelViewSet):
queryset = PowerPanel.objects.select_related('site', 'rack_group')
queryset = PowerPanel.objects.select_related(
'site', 'rack_group'
).annotate(
powerfeed_count=Count('powerfeeds')
)
serializer_class = serializers.PowerPanelSerializer
filterset_class = filters.PowerPanelFilter

View File

@ -47,7 +47,7 @@ class RegionTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'site_count', 'slug', 'url']
)
def test_create_region(self):
@ -285,7 +285,7 @@ class RackGroupTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'rack_count', 'slug', 'url']
)
def test_create_rackgroup(self):
@ -393,7 +393,7 @@ class RackRoleTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'rack_count', 'slug', 'url']
)
def test_create_rackrole(self):
@ -520,7 +520,7 @@ class RackTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['display_name', 'id', 'name', 'url']
['device_count', 'display_name', 'id', 'name', 'url']
)
def test_create_rack(self):
@ -746,7 +746,7 @@ class ManufacturerTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['devicetype_count', 'id', 'name', 'slug', 'url']
)
def test_create_manufacturer(self):
@ -855,7 +855,7 @@ class DeviceTypeTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['display_name', 'id', 'manufacturer', 'model', 'slug', 'url']
['device_count', 'display_name', 'id', 'manufacturer', 'model', 'slug', 'url']
)
def test_create_devicetype(self):
@ -1569,7 +1569,7 @@ class DeviceRoleTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['device_count', 'id', 'name', 'slug', 'url', 'virtualmachine_count']
)
def test_create_devicerole(self):
@ -1677,7 +1677,7 @@ class PlatformTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['device_count', 'id', 'name', 'slug', 'url', 'virtualmachine_count']
)
def test_create_platform(self):
@ -3457,7 +3457,7 @@ class VirtualChassisTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'master', 'url']
['id', 'master', 'member_count', 'url']
)
def test_create_virtualchassis(self):
@ -3575,7 +3575,7 @@ class PowerPanelTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'url']
['id', 'name', 'powerfeed_count', 'url']
)
def test_create_powerpanel(self):

View File

@ -21,10 +21,11 @@ __all__ = [
class NestedVRFSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
prefix_count = serializers.IntegerField(read_only=True)
class Meta:
model = VRF
fields = ['id', 'url', 'name', 'rd']
fields = ['id', 'url', 'name', 'rd', 'prefix_count']
#
@ -33,10 +34,11 @@ class NestedVRFSerializer(WritableNestedSerializer):
class NestedRIRSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
aggregate_count = serializers.IntegerField(read_only=True)
class Meta:
model = RIR
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'aggregate_count']
class NestedAggregateSerializer(WritableNestedSerializer):
@ -53,18 +55,21 @@ class NestedAggregateSerializer(WritableNestedSerializer):
class NestedRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
prefix_count = serializers.IntegerField(read_only=True)
vlan_count = serializers.IntegerField(read_only=True)
class Meta:
model = Role
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'prefix_count', 'vlan_count']
class NestedVLANGroupSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
vlan_count = serializers.IntegerField(read_only=True)
class Meta:
model = VLANGroup
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'vlan_count']
class NestedVLANSerializer(WritableNestedSerializer):

View File

@ -25,12 +25,13 @@ from .nested_serializers import *
class VRFSerializer(TaggitSerializer, CustomFieldModelSerializer):
tenant = NestedTenantSerializer(required=False, allow_null=True)
tags = TagListSerializerField(required=False)
prefix_count = serializers.IntegerField(read_only=True)
class Meta:
model = VRF
fields = [
'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'tags', 'display_name', 'custom_fields',
'created', 'last_updated',
'created', 'last_updated', 'prefix_count',
]
@ -39,10 +40,11 @@ class VRFSerializer(TaggitSerializer, CustomFieldModelSerializer):
#
class RIRSerializer(ValidatedModelSerializer):
aggregate_count = serializers.IntegerField(read_only=True)
class Meta:
model = RIR
fields = ['id', 'name', 'slug', 'is_private']
fields = ['id', 'name', 'slug', 'is_private', 'aggregate_count']
class AggregateSerializer(TaggitSerializer, CustomFieldModelSerializer):
@ -63,18 +65,21 @@ class AggregateSerializer(TaggitSerializer, CustomFieldModelSerializer):
#
class RoleSerializer(ValidatedModelSerializer):
prefix_count = serializers.IntegerField(read_only=True)
vlan_count = serializers.IntegerField(read_only=True)
class Meta:
model = Role
fields = ['id', 'name', 'slug', 'weight']
fields = ['id', 'name', 'slug', 'weight', 'prefix_count', 'vlan_count']
class VLANGroupSerializer(ValidatedModelSerializer):
site = NestedSiteSerializer(required=False, allow_null=True)
vlan_count = serializers.IntegerField(read_only=True)
class Meta:
model = VLANGroup
fields = ['id', 'name', 'slug', 'site']
fields = ['id', 'name', 'slug', 'site', 'vlan_count']
validators = []
def validate(self, data):

View File

@ -1,4 +1,5 @@
from django.conf import settings
from django.db.models import Count, OuterRef, Subquery
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.decorators import action
@ -31,7 +32,9 @@ class IPAMFieldChoicesViewSet(FieldChoicesViewSet):
#
class VRFViewSet(CustomFieldModelViewSet):
queryset = VRF.objects.select_related('tenant').prefetch_related('tags')
queryset = VRF.objects.select_related('tenant').prefetch_related('tags').annotate(
prefix_count=Count('prefixes')
)
serializer_class = serializers.VRFSerializer
filterset_class = filters.VRFFilter
@ -41,7 +44,9 @@ class VRFViewSet(CustomFieldModelViewSet):
#
class RIRViewSet(ModelViewSet):
queryset = RIR.objects.all()
queryset = RIR.objects.annotate(
aggregate_count=Count('aggregates')
)
serializer_class = serializers.RIRSerializer
filterset_class = filters.RIRFilter
@ -61,7 +66,10 @@ class AggregateViewSet(CustomFieldModelViewSet):
#
class RoleViewSet(ModelViewSet):
queryset = Role.objects.all()
queryset = Role.objects.annotate(
prefix_count=Count('prefixes', distinct=True),
vlan_count=Count('vlans', distinct=True)
)
serializer_class = serializers.RoleSerializer
filterset_class = filters.RoleFilter
@ -71,7 +79,11 @@ class RoleViewSet(ModelViewSet):
#
class PrefixViewSet(CustomFieldModelViewSet):
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role').prefetch_related('tags')
queryset = Prefix.objects.select_related(
'site', 'vrf__tenant', 'tenant', 'vlan', 'role'
).prefetch_related(
'tags'
)
serializer_class = serializers.PrefixSerializer
filterset_class = filters.PrefixFilter
@ -263,7 +275,9 @@ class IPAddressViewSet(CustomFieldModelViewSet):
#
class VLANGroupViewSet(ModelViewSet):
queryset = VLANGroup.objects.select_related('site')
queryset = VLANGroup.objects.select_related('site').annotate(
vlan_count=Count('vlans')
)
serializer_class = serializers.VLANGroupSerializer
filterset_class = filters.VLANGroupFilter

View File

@ -39,7 +39,7 @@ class VRFTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'rd', 'url']
['id', 'name', 'prefix_count', 'rd', 'url']
)
def test_create_vrf(self):
@ -147,7 +147,7 @@ class RIRTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['aggregate_count', 'id', 'name', 'slug', 'url']
)
def test_create_rir(self):
@ -351,7 +351,7 @@ class RoleTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'prefix_count', 'slug', 'url', 'vlan_count']
)
def test_create_role(self):
@ -790,7 +790,7 @@ class VLANGroupTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'slug', 'url', 'vlan_count']
)
def test_create_vlangroup(self):

View File

@ -1,4 +1,5 @@
from django.conf import settings
from django.db.models import QuerySet
from rest_framework import authentication, exceptions
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.permissions import DjangoModelPermissions, SAFE_METHODS
@ -96,13 +97,8 @@ class OptionalLimitOffsetPagination(LimitOffsetPagination):
def paginate_queryset(self, queryset, request, view=None):
if hasattr(queryset, 'all'):
# TODO: This breaks filtering by annotated values
# Make a clone of the queryset with any annotations stripped (performance hack)
qs = queryset.all()
qs.query.annotations.clear()
self.count = qs.count()
if type(queryset) is QuerySet:
self.count = queryset.count()
else:
# We're dealing with an iterable, not a QuerySet
self.count = len(queryset)

View File

@ -10,7 +10,8 @@ __all__ = [
class NestedSecretRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='secrets-api:secretrole-detail')
secret_count = serializers.IntegerField(read_only=True)
class Meta:
model = SecretRole
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'secret_count']

View File

@ -14,10 +14,11 @@ from .nested_serializers import *
#
class SecretRoleSerializer(ValidatedModelSerializer):
secret_count = serializers.IntegerField(read_only=True)
class Meta:
model = SecretRole
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'secret_count']
class SecretSerializer(TaggitSerializer, CustomFieldModelSerializer):

View File

@ -1,6 +1,7 @@
import base64
from Crypto.PublicKey import RSA
from django.db.models import Count
from django.http import HttpResponseBadRequest
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated
@ -32,7 +33,9 @@ class SecretsFieldChoicesViewSet(FieldChoicesViewSet):
#
class SecretRoleViewSet(ModelViewSet):
queryset = SecretRole.objects.all()
queryset = SecretRole.objects.annotate(
secret_count=Count('secrets')
)
serializer_class = serializers.SecretRoleSerializer
permission_classes = [IsAuthenticated]
filterset_class = filters.SecretRoleFilter

View File

@ -78,7 +78,7 @@ class SecretRoleTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'secret_count', 'slug', 'url']
)
def test_create_secretrole(self):

View File

@ -15,10 +15,11 @@ __all__ = [
class NestedTenantGroupSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
tenant_count = serializers.IntegerField(read_only=True)
class Meta:
model = TenantGroup
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'tenant_count']
class NestedTenantSerializer(WritableNestedSerializer):

View File

@ -1,3 +1,4 @@
from rest_framework import serializers
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
from extras.api.customfields import CustomFieldModelSerializer
@ -11,10 +12,11 @@ from .nested_serializers import *
#
class TenantGroupSerializer(ValidatedModelSerializer):
tenant_count = serializers.IntegerField(read_only=True)
class Meta:
model = TenantGroup
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'tenant_count']
class TenantSerializer(TaggitSerializer, CustomFieldModelSerializer):

View File

@ -1,3 +1,5 @@
from django.db.models import Count
from extras.api.views import CustomFieldModelViewSet
from tenancy import filters
from tenancy.models import Tenant, TenantGroup
@ -18,7 +20,9 @@ class TenancyFieldChoicesViewSet(FieldChoicesViewSet):
#
class TenantGroupViewSet(ModelViewSet):
queryset = TenantGroup.objects.all()
queryset = TenantGroup.objects.annotate(
tenant_count=Count('tenants')
)
serializer_class = serializers.TenantGroupSerializer
filterset_class = filters.TenantGroupFilter

View File

@ -36,7 +36,7 @@ class TenantGroupTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'slug', 'tenant_count', 'url']
)
def test_create_tenantgroup(self):

View File

@ -19,26 +19,29 @@ __all__ = [
class NestedClusterTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
cluster_count = serializers.IntegerField(read_only=True)
class Meta:
model = ClusterType
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'cluster_count']
class NestedClusterGroupSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
cluster_count = serializers.IntegerField(read_only=True)
class Meta:
model = ClusterGroup
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'cluster_count']
class NestedClusterSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = Cluster
fields = ['id', 'url', 'name']
fields = ['id', 'url', 'name', 'virtualmachine_count']
#

View File

@ -20,17 +20,19 @@ from .nested_serializers import *
#
class ClusterTypeSerializer(ValidatedModelSerializer):
cluster_count = serializers.IntegerField(read_only=True)
class Meta:
model = ClusterType
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'cluster_count']
class ClusterGroupSerializer(ValidatedModelSerializer):
cluster_count = serializers.IntegerField(read_only=True)
class Meta:
model = ClusterGroup
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'cluster_count']
class ClusterSerializer(TaggitSerializer, CustomFieldModelSerializer):
@ -38,11 +40,13 @@ class ClusterSerializer(TaggitSerializer, CustomFieldModelSerializer):
group = NestedClusterGroupSerializer(required=False, allow_null=True)
site = NestedSiteSerializer(required=False, allow_null=True)
tags = TagListSerializerField(required=False)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = Cluster
fields = [
'id', 'name', 'type', 'group', 'site', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'virtualmachine_count',
]

View File

@ -1,3 +1,5 @@
from django.db.models import Count
from dcim.models import Interface
from extras.api.views import CustomFieldModelViewSet
from utilities.api import FieldChoicesViewSet, ModelViewSet
@ -21,19 +23,25 @@ class VirtualizationFieldChoicesViewSet(FieldChoicesViewSet):
#
class ClusterTypeViewSet(ModelViewSet):
queryset = ClusterType.objects.all()
queryset = ClusterType.objects.annotate(
cluster_count=Count('clusters')
)
serializer_class = serializers.ClusterTypeSerializer
filterset_class = filters.ClusterTypeFilter
class ClusterGroupViewSet(ModelViewSet):
queryset = ClusterGroup.objects.all()
queryset = ClusterGroup.objects.annotate(
cluster_count=Count('clusters')
)
serializer_class = serializers.ClusterGroupSerializer
filterset_class = filters.ClusterGroupFilter
class ClusterViewSet(CustomFieldModelViewSet):
queryset = Cluster.objects.select_related('type', 'group').prefetch_related('tags')
queryset = Cluster.objects.select_related('type', 'group').prefetch_related('tags').annotate(
virtualmachine_count=Count('virtual_machines')
)
serializer_class = serializers.ClusterSerializer
filterset_class = filters.ClusterFilter

View File

@ -40,7 +40,7 @@ class ClusterTypeTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['cluster_count', 'id', 'name', 'slug', 'url']
)
def test_create_clustertype(self):
@ -141,7 +141,7 @@ class ClusterGroupTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['cluster_count', 'id', 'name', 'slug', 'url']
)
def test_create_clustergroup(self):
@ -245,7 +245,7 @@ class ClusterTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'url']
['id', 'name', 'url', 'virtualmachine_count']
)
def test_create_cluster(self):