From 701f461225a8a6bd5c1119550b3e820e372daa6c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 14 Feb 2024 12:02:14 -0500 Subject: [PATCH] Add RelatedObjectCountFields to serializers; remove static annotations from querysets --- netbox/circuits/api/nested_serializers.py | 8 +-- netbox/circuits/api/serializers.py | 12 ++-- netbox/circuits/api/views.py | 9 +-- netbox/core/api/serializers.py | 6 +- netbox/core/api/views.py | 5 +- netbox/dcim/api/nested_serializers.py | 24 ++++---- netbox/dcim/api/serializers.py | 58 ++++++++++++------- netbox/dcim/api/views.py | 54 ++++------------- netbox/extras/api/serializers.py | 6 +- netbox/extras/api/views.py | 6 +- netbox/ipam/api/nested_serializers.py | 11 ++-- netbox/ipam/api/serializers.py | 32 ++++++---- netbox/ipam/api/views.py | 24 ++------ netbox/tenancy/api/serializers.py | 24 ++++---- netbox/tenancy/api/views.py | 18 +----- .../virtualization/api/nested_serializers.py | 7 ++- netbox/virtualization/api/serializers.py | 16 +++-- netbox/virtualization/api/views.py | 15 +---- netbox/vpn/api/nested_serializers.py | 3 +- netbox/vpn/api/serializers.py | 11 +++- netbox/vpn/api/views.py | 9 +-- 21 files changed, 157 insertions(+), 201 deletions(-) diff --git a/netbox/circuits/api/nested_serializers.py b/netbox/circuits/api/nested_serializers.py index c7aa583fa..034970124 100644 --- a/netbox/circuits/api/nested_serializers.py +++ b/netbox/circuits/api/nested_serializers.py @@ -1,8 +1,8 @@ -from drf_spectacular.utils import extend_schema_field, extend_schema_serializer -from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers from circuits.models import * +from netbox.api.fields import RelatedObjectCountField from netbox.api.serializers import WritableNestedSerializer __all__ = [ @@ -36,7 +36,7 @@ class NestedProviderNetworkSerializer(WritableNestedSerializer): ) class NestedProviderSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail') - circuit_count = serializers.IntegerField(read_only=True) + circuit_count = RelatedObjectCountField('circuits.circuit', 'provider') class Meta: model = Provider @@ -64,7 +64,7 @@ class NestedProviderAccountSerializer(WritableNestedSerializer): ) class NestedCircuitTypeSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail') - circuit_count = serializers.IntegerField(read_only=True) + circuit_count = RelatedObjectCountField('circuits.circuit', 'type') class Meta: model = CircuitType diff --git a/netbox/circuits/api/serializers.py b/netbox/circuits/api/serializers.py index 5223de339..f4aba713e 100644 --- a/netbox/circuits/api/serializers.py +++ b/netbox/circuits/api/serializers.py @@ -6,7 +6,7 @@ from dcim.api.nested_serializers import NestedSiteSerializer from dcim.api.serializers import CabledObjectSerializer from ipam.models import ASN from ipam.api.nested_serializers import NestedASNSerializer -from netbox.api.fields import ChoiceField, SerializedPKRelatedField +from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer from tenancy.api.nested_serializers import NestedTenantSerializer from .nested_serializers import * @@ -32,7 +32,7 @@ class ProviderSerializer(NetBoxModelSerializer): ) # Related object counts - circuit_count = serializers.IntegerField(read_only=True) + circuit_count = RelatedObjectCountField('circuits.circuit', 'provider') class Meta: model = Provider @@ -80,13 +80,15 @@ class ProviderNetworkSerializer(NetBoxModelSerializer): class CircuitTypeSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail') - circuit_count = serializers.IntegerField(read_only=True) + + # Related object counts + circuit_count = RelatedObjectCountField('circuits.circuit', 'type') class Meta: model = CircuitType fields = [ - 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created', 'last_updated', - 'circuit_count', + 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created', + 'last_updated', 'circuit_count', ] diff --git a/netbox/circuits/api/views.py b/netbox/circuits/api/views.py index 71db8f8f2..fffb59a57 100644 --- a/netbox/circuits/api/views.py +++ b/netbox/circuits/api/views.py @@ -4,7 +4,6 @@ from circuits import filtersets from circuits.models import * from dcim.api.views import PassThroughPortMixin from netbox.api.viewsets import NetBoxModelViewSet -from utilities.utils import count_related from . import serializers @@ -21,9 +20,7 @@ class CircuitsRootView(APIRootView): # class ProviderViewSet(NetBoxModelViewSet): - queryset = Provider.objects.annotate( - circuit_count=count_related(Circuit, 'provider') - ) + queryset = Provider.objects.all() serializer_class = serializers.ProviderSerializer filterset_class = filtersets.ProviderFilterSet @@ -33,9 +30,7 @@ class ProviderViewSet(NetBoxModelViewSet): # class CircuitTypeViewSet(NetBoxModelViewSet): - queryset = CircuitType.objects.annotate( - circuit_count=count_related(Circuit, 'type') - ) + queryset = CircuitType.objects.all() serializer_class = serializers.CircuitTypeSerializer filterset_class = filtersets.CircuitTypeFilterSet diff --git a/netbox/core/api/serializers.py b/netbox/core/api/serializers.py index a16a06d62..93b5ce98a 100644 --- a/netbox/core/api/serializers.py +++ b/netbox/core/api/serializers.py @@ -2,7 +2,7 @@ from rest_framework import serializers from core.choices import * from core.models import * -from netbox.api.fields import ChoiceField, ContentTypeField +from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer from netbox.utils import get_data_backend_choices from users.api.nested_serializers import NestedUserSerializer @@ -28,9 +28,7 @@ class DataSourceSerializer(NetBoxModelSerializer): ) # Related object counts - file_count = serializers.IntegerField( - read_only=True - ) + file_count = RelatedObjectCountField('core.datafile', 'source') class Meta: model = DataSource diff --git a/netbox/core/api/views.py b/netbox/core/api/views.py index 9c2e23f2e..3fddfd691 100644 --- a/netbox/core/api/views.py +++ b/netbox/core/api/views.py @@ -9,7 +9,6 @@ from rest_framework.viewsets import ReadOnlyModelViewSet from core import filtersets from core.models import * from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet -from utilities.utils import count_related from . import serializers @@ -22,9 +21,7 @@ class CoreRootView(APIRootView): class DataSourceViewSet(NetBoxModelViewSet): - queryset = DataSource.objects.annotate( - file_count=count_related(DataFile, 'source') - ) + queryset = DataSource.objects.all() serializer_class = serializers.DataSourceSerializer filterset_class = filtersets.DataSourceFilterSet diff --git a/netbox/dcim/api/nested_serializers.py b/netbox/dcim/api/nested_serializers.py index c8440612d..0fc5cedac 100644 --- a/netbox/dcim/api/nested_serializers.py +++ b/netbox/dcim/api/nested_serializers.py @@ -2,7 +2,8 @@ from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers from dcim import models -from netbox.api.serializers import BaseModelSerializer, WritableNestedSerializer +from netbox.api.fields import RelatedObjectCountField +from netbox.api.serializers import WritableNestedSerializer __all__ = [ 'ComponentNestedModuleSerializer', @@ -110,7 +111,7 @@ class NestedLocationSerializer(WritableNestedSerializer): ) class NestedRackRoleSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail') - rack_count = serializers.IntegerField(read_only=True) + rack_count = RelatedObjectCountField('dcim.rack', 'role') class Meta: model = models.RackRole @@ -122,7 +123,7 @@ class NestedRackRoleSerializer(WritableNestedSerializer): ) class NestedRackSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail') - device_count = serializers.IntegerField(read_only=True) + device_count = RelatedObjectCountField('dcim.device', 'rack') class Meta: model = models.Rack @@ -150,7 +151,7 @@ class NestedRackReservationSerializer(WritableNestedSerializer): ) class NestedManufacturerSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail') - devicetype_count = serializers.IntegerField(read_only=True) + devicetype_count = RelatedObjectCountField('dcim.devicetype', 'manufacturer') class Meta: model = models.Manufacturer @@ -163,7 +164,7 @@ class NestedManufacturerSerializer(WritableNestedSerializer): class NestedDeviceTypeSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail') manufacturer = NestedManufacturerSerializer(read_only=True) - device_count = serializers.IntegerField(read_only=True) + device_count = RelatedObjectCountField('dcim.device', 'device_type') class Meta: model = models.DeviceType @@ -173,7 +174,6 @@ class NestedDeviceTypeSerializer(WritableNestedSerializer): class NestedModuleTypeSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail') manufacturer = NestedManufacturerSerializer(read_only=True) - # module_count = serializers.IntegerField(read_only=True) class Meta: model = models.ModuleType @@ -274,8 +274,8 @@ class NestedInventoryItemTemplateSerializer(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) + device_count = RelatedObjectCountField('dcim.device', 'role') + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'role') class Meta: model = models.DeviceRole @@ -287,8 +287,8 @@ class NestedDeviceRoleSerializer(WritableNestedSerializer): ) 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) + device_count = RelatedObjectCountField('dcim.device', 'platform') + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'platform') class Meta: model = models.Platform @@ -445,7 +445,7 @@ class NestedInventoryItemSerializer(WritableNestedSerializer): ) class NestedInventoryItemRoleSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail') - inventoryitem_count = serializers.IntegerField(read_only=True) + inventoryitem_count = RelatedObjectCountField('dcim.inventoryitem', 'role') class Meta: model = models.InventoryItemRole @@ -490,7 +490,7 @@ class NestedVirtualChassisSerializer(WritableNestedSerializer): ) class NestedPowerPanelSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail') - powerfeed_count = serializers.IntegerField(read_only=True) + powerfeed_count = RelatedObjectCountField('dcim.powerfeed', 'power_panel') class Meta: model = models.PowerPanel diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 09933f2de..af3dfee6a 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -15,7 +15,7 @@ from ipam.api.nested_serializers import ( NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer, ) from ipam.models import ASN, VLAN -from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField +from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField from netbox.api.serializers import ( GenericObjectSerializer, NestedGroupModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer, WritableNestedSerializer, @@ -144,12 +144,12 @@ class SiteSerializer(NetBoxModelSerializer): ) # Related object counts - circuit_count = serializers.IntegerField(read_only=True) - device_count = serializers.IntegerField(read_only=True) - prefix_count = serializers.IntegerField(read_only=True) - rack_count = serializers.IntegerField(read_only=True) - virtualmachine_count = serializers.IntegerField(read_only=True) - vlan_count = serializers.IntegerField(read_only=True) + circuit_count = RelatedObjectCountField('circuits.circuit', 'terminations__site') + device_count = RelatedObjectCountField('dcim.device', 'site') + prefix_count = RelatedObjectCountField('ipam.prefix', 'site') + rack_count = RelatedObjectCountField('dcim.rack', 'site') + vlan_count = RelatedObjectCountField('ipam.vlan', 'site') + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'site') class Meta: model = Site @@ -184,7 +184,9 @@ class LocationSerializer(NestedGroupModelSerializer): class RackRoleSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail') - rack_count = serializers.IntegerField(read_only=True) + + # Related object counts + rack_count = RelatedObjectCountField('dcim.rack', 'role') class Meta: model = RackRole @@ -207,8 +209,10 @@ class RackSerializer(NetBoxModelSerializer): width = ChoiceField(choices=RackWidthChoices, required=False) outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False, allow_null=True) weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True) - device_count = serializers.IntegerField(read_only=True) - powerfeed_count = serializers.IntegerField(read_only=True) + + # Related object counts + device_count = RelatedObjectCountField('dcim.device', 'rack') + powerfeed_count = RelatedObjectCountField('dcim.powerfeed', 'rack') class Meta: model = Rack @@ -299,9 +303,11 @@ class RackElevationDetailFilterSerializer(serializers.Serializer): class ManufacturerSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail') - devicetype_count = serializers.IntegerField(read_only=True) - inventoryitem_count = serializers.IntegerField(read_only=True) - platform_count = serializers.IntegerField(read_only=True) + + # Related object counts + devicetype_count = RelatedObjectCountField('dcim.devicetype', 'manufacturer') + inventoryitem_count = RelatedObjectCountField('dcim.inventoryitem', 'manufacturer') + platform_count = RelatedObjectCountField('dcim.platform', 'manufacturer') class Meta: model = Manufacturer @@ -325,7 +331,6 @@ class DeviceTypeSerializer(NetBoxModelSerializer): subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False, allow_null=True) airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False, allow_null=True) 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) @@ -339,6 +344,9 @@ class DeviceTypeSerializer(NetBoxModelSerializer): module_bay_template_count = serializers.IntegerField(read_only=True) inventory_item_template_count = serializers.IntegerField(read_only=True) + # Related object counts + device_count = RelatedObjectCountField('dcim.device', 'device_type') + class Meta: model = DeviceType fields = [ @@ -636,8 +644,10 @@ class InventoryItemTemplateSerializer(ValidatedModelSerializer): class DeviceRoleSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail') config_template = NestedConfigTemplateSerializer(required=False, allow_null=True, default=None) - device_count = serializers.IntegerField(read_only=True) - virtualmachine_count = serializers.IntegerField(read_only=True) + + # Related object counts + device_count = RelatedObjectCountField('dcim.device', 'role') + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'role') class Meta: model = DeviceRole @@ -651,8 +661,10 @@ class PlatformSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail') manufacturer = NestedManufacturerSerializer(required=False, allow_null=True) config_template = NestedConfigTemplateSerializer(required=False, allow_null=True, default=None) - device_count = serializers.IntegerField(read_only=True) - virtualmachine_count = serializers.IntegerField(read_only=True) + + # Related object counts + device_count = RelatedObjectCountField('dcim.device', 'platform') + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'platform') class Meta: model = Platform @@ -761,7 +773,7 @@ class VirtualDeviceContextSerializer(NetBoxModelSerializer): status = ChoiceField(choices=VirtualDeviceContextStatusChoices) # Related object counts - interface_count = serializers.IntegerField(read_only=True) + interface_count = RelatedObjectCountField('dcim.interface', 'vdcs') class Meta: model = VirtualDeviceContext @@ -1092,7 +1104,9 @@ class InventoryItemSerializer(NetBoxModelSerializer): class InventoryItemRoleSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail') - inventoryitem_count = serializers.IntegerField(read_only=True) + + # Related object counts + inventoryitem_count = RelatedObjectCountField('dcim.inventoryitem', 'role') class Meta: model = InventoryItemRole @@ -1204,7 +1218,9 @@ class PowerPanelSerializer(NetBoxModelSerializer): allow_null=True, default=None ) - powerfeed_count = serializers.IntegerField(read_only=True) + + # Related object counts + powerfeed_count = RelatedObjectCountField('dcim.powerfeed', 'power_panel') class Meta: model = PowerPanel diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index c429980c4..04333c084 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -7,13 +7,11 @@ from rest_framework.response import Response from rest_framework.routers import APIRootView from rest_framework.viewsets import ViewSet -from circuits.models import Circuit from dcim import filtersets from dcim.constants import CABLE_TRACE_SVG_DEFAULT_WIDTH from dcim.models import * from dcim.svg import CableTraceSVG from extras.api.mixins import ConfigContextQuerySetMixin, RenderConfigMixin -from ipam.models import Prefix, VLAN from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired from netbox.api.metadata import ContentTypeMetadata from netbox.api.pagination import StripCountAnnotationsPaginator @@ -22,8 +20,6 @@ from netbox.api.viewsets.mixins import SequentialBulkCreatesMixin from netbox.constants import NESTED_SERIALIZER_PREFIX from utilities.api import get_serializer_for_model from utilities.query_functions import CollateAsChar -from utilities.utils import count_related -from virtualization.models import VirtualMachine from . import serializers from .exceptions import MissingFilterException @@ -129,14 +125,7 @@ class SiteGroupViewSet(MPTTLockedMixin, NetBoxModelViewSet): # class SiteViewSet(NetBoxModelViewSet): - queryset = Site.objects.annotate( - device_count=count_related(Device, 'site'), - rack_count=count_related(Rack, 'site'), - prefix_count=count_related(Prefix, 'site'), - vlan_count=count_related(VLAN, 'site'), - circuit_count=count_related(Circuit, 'terminations__site'), - virtualmachine_count=count_related(VirtualMachine, 'cluster__site') - ) + queryset = Site.objects.all() serializer_class = serializers.SiteSerializer filterset_class = filtersets.SiteFilterSet @@ -168,9 +157,7 @@ class LocationViewSet(MPTTLockedMixin, NetBoxModelViewSet): # class RackRoleViewSet(NetBoxModelViewSet): - queryset = RackRole.objects.annotate( - rack_count=count_related(Rack, 'role') - ) + queryset = RackRole.objects.all() serializer_class = serializers.RackRoleSerializer filterset_class = filtersets.RackRoleFilterSet @@ -180,10 +167,7 @@ class RackRoleViewSet(NetBoxModelViewSet): # class RackViewSet(NetBoxModelViewSet): - queryset = Rack.objects.annotate( - device_count=count_related(Device, 'rack'), - powerfeed_count=count_related(PowerFeed, 'rack') - ) + queryset = Rack.objects.all() serializer_class = serializers.RackSerializer filterset_class = filtersets.RackFilterSet @@ -255,11 +239,7 @@ class RackReservationViewSet(NetBoxModelViewSet): # class ManufacturerViewSet(NetBoxModelViewSet): - queryset = Manufacturer.objects.annotate( - devicetype_count=count_related(DeviceType, 'manufacturer'), - inventoryitem_count=count_related(InventoryItem, 'manufacturer'), - platform_count=count_related(Platform, 'manufacturer') - ) + queryset = Manufacturer.objects.all() serializer_class = serializers.ManufacturerSerializer filterset_class = filtersets.ManufacturerFilterSet @@ -269,9 +249,7 @@ class ManufacturerViewSet(NetBoxModelViewSet): # class DeviceTypeViewSet(NetBoxModelViewSet): - queryset = DeviceType.objects.annotate( - device_count=count_related(Device, 'device_type') - ) + queryset = DeviceType.objects.all() serializer_class = serializers.DeviceTypeSerializer filterset_class = filtersets.DeviceTypeFilterSet @@ -351,10 +329,7 @@ class InventoryItemTemplateViewSet(MPTTLockedMixin, NetBoxModelViewSet): # class DeviceRoleViewSet(NetBoxModelViewSet): - queryset = DeviceRole.objects.annotate( - device_count=count_related(Device, 'role'), - virtualmachine_count=count_related(VirtualMachine, 'role') - ) + queryset = DeviceRole.objects.all() serializer_class = serializers.DeviceRoleSerializer filterset_class = filtersets.DeviceRoleFilterSet @@ -364,10 +339,7 @@ class DeviceRoleViewSet(NetBoxModelViewSet): # class PlatformViewSet(NetBoxModelViewSet): - queryset = Platform.objects.annotate( - device_count=count_related(Device, 'platform'), - virtualmachine_count=count_related(VirtualMachine, 'platform') - ) + queryset = Platform.objects.all() serializer_class = serializers.PlatformSerializer filterset_class = filtersets.PlatformFilterSet @@ -410,9 +382,7 @@ class DeviceViewSet( class VirtualDeviceContextViewSet(NetBoxModelViewSet): - queryset = VirtualDeviceContext.objects.annotate( - interface_count=count_related(Interface, 'vdcs'), - ) + queryset = VirtualDeviceContext.objects.all() serializer_class = serializers.VirtualDeviceContextSerializer filterset_class = filtersets.VirtualDeviceContextFilterSet @@ -513,9 +483,7 @@ class InventoryItemViewSet(MPTTLockedMixin, NetBoxModelViewSet): # class InventoryItemRoleViewSet(NetBoxModelViewSet): - queryset = InventoryItemRole.objects.annotate( - inventoryitem_count=count_related(InventoryItem, 'role') - ) + queryset = InventoryItemRole.objects.all() serializer_class = serializers.InventoryItemRoleSerializer filterset_class = filtersets.InventoryItemRoleFilterSet @@ -552,9 +520,7 @@ class VirtualChassisViewSet(NetBoxModelViewSet): # class PowerPanelViewSet(NetBoxModelViewSet): - queryset = PowerPanel.objects.annotate( - powerfeed_count=count_related(PowerFeed, 'power_panel') - ) + queryset = PowerPanel.objects.all() serializer_class = serializers.PowerPanelSerializer filterset_class = filtersets.PowerPanelFilterSet diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index e2f30e8b0..9abf916f0 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -3,7 +3,6 @@ from django.core.exceptions import ObjectDoesNotExist from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema_field from rest_framework import serializers -from rest_framework.fields import ListField from core.api.nested_serializers import NestedDataSourceSerializer, NestedDataFileSerializer, NestedJobSerializer from core.api.serializers import JobSerializer @@ -16,7 +15,7 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site from extras.choices import * from extras.models import * from netbox.api.exceptions import SerializerNotFound -from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField +from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer from netbox.api.serializers.features import TaggableModelSerializer from netbox.constants import NESTED_SERIALIZER_PREFIX @@ -290,6 +289,9 @@ class TagSerializer(ValidatedModelSerializer): ) tagged_items = serializers.IntegerField(read_only=True) + # Related object counts + tagged_items = RelatedObjectCountField('extras.taggeditem', 'tag') + class Meta: model = Tag fields = [ diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 32a569d52..db26a6f6c 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -23,7 +23,7 @@ from netbox.api.metadata import ContentTypeMetadata from netbox.api.renderers import TextRenderer from netbox.api.viewsets import NetBoxModelViewSet from utilities.exceptions import RQWorkerNotRunningException -from utilities.utils import copy_safe_request, count_related +from utilities.utils import copy_safe_request from . import serializers from .mixins import ConfigTemplateRenderMixin @@ -147,9 +147,7 @@ class BookmarkViewSet(NetBoxModelViewSet): # class TagViewSet(NetBoxModelViewSet): - queryset = Tag.objects.annotate( - tagged_items=count_related(TaggedItem, 'tag') - ) + queryset = Tag.objects.all() serializer_class = serializers.TagSerializer filterset_class = filtersets.TagFilterSet diff --git a/netbox/ipam/api/nested_serializers.py b/netbox/ipam/api/nested_serializers.py index c012eca6d..156f8897e 100644 --- a/netbox/ipam/api/nested_serializers.py +++ b/netbox/ipam/api/nested_serializers.py @@ -2,6 +2,7 @@ from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers from ipam import models +from netbox.api.fields import RelatedObjectCountField from netbox.api.serializers import WritableNestedSerializer from .field_serializers import IPAddressField @@ -58,7 +59,7 @@ class NestedASNSerializer(WritableNestedSerializer): ) class NestedVRFSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail') - prefix_count = serializers.IntegerField(read_only=True) + prefix_count = RelatedObjectCountField('ipam.prefix', 'vrf') class Meta: model = models.VRF @@ -86,7 +87,7 @@ class NestedRouteTargetSerializer(WritableNestedSerializer): ) class NestedRIRSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail') - aggregate_count = serializers.IntegerField(read_only=True) + aggregate_count = RelatedObjectCountField('ipam.aggregate', 'rir') class Meta: model = models.RIR @@ -132,8 +133,8 @@ class NestedFHRPGroupAssignmentSerializer(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) + prefix_count = RelatedObjectCountField('ipam.prefix', 'role') + vlan_count = RelatedObjectCountField('ipam.vlan', 'role') class Meta: model = models.Role @@ -145,7 +146,7 @@ class NestedRoleSerializer(WritableNestedSerializer): ) class NestedVLANGroupSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail') - vlan_count = serializers.IntegerField(read_only=True) + vlan_count = RelatedObjectCountField('ipam.vlan', 'group') class Meta: model = models.VLANGroup diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 33aa55a93..76a323c38 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -6,7 +6,7 @@ from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerial from ipam.choices import * from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES from ipam.models import * -from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField +from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField from netbox.api.serializers import NetBoxModelSerializer from netbox.constants import NESTED_SERIALIZER_PREFIX from tenancy.api.nested_serializers import NestedTenantSerializer @@ -43,8 +43,10 @@ class ASNSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail') rir = NestedRIRSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) - site_count = serializers.IntegerField(read_only=True) - provider_count = serializers.IntegerField(read_only=True) + + # Related object counts + site_count = RelatedObjectCountField('dcim.site', 'asns') + provider_count = RelatedObjectCountField('circuits.provider', 'asns') class Meta: model = ASN @@ -90,8 +92,10 @@ class VRFSerializer(NetBoxModelSerializer): required=False, many=True ) - ipaddress_count = serializers.IntegerField(read_only=True) - prefix_count = serializers.IntegerField(read_only=True) + + # Related object counts + ipaddress_count = RelatedObjectCountField('ipam.ipaddress', 'vrf') + prefix_count = RelatedObjectCountField('ipam.prefix', 'vrf') class Meta: model = VRF @@ -124,7 +128,9 @@ class RouteTargetSerializer(NetBoxModelSerializer): class RIRSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail') - aggregate_count = serializers.IntegerField(read_only=True) + + # Related object counts + aggregate_count = RelatedObjectCountField('ipam.aggregate', 'rir') class Meta: model = RIR @@ -195,8 +201,10 @@ class FHRPGroupAssignmentSerializer(NetBoxModelSerializer): class RoleSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail') - prefix_count = serializers.IntegerField(read_only=True) - vlan_count = serializers.IntegerField(read_only=True) + + # Related object counts + prefix_count = RelatedObjectCountField('ipam.prefix', 'role') + vlan_count = RelatedObjectCountField('ipam.vlan', 'role') class Meta: model = Role @@ -218,9 +226,11 @@ class VLANGroupSerializer(NetBoxModelSerializer): ) scope_id = serializers.IntegerField(allow_null=True, required=False, default=None) scope = serializers.SerializerMethodField(read_only=True) - vlan_count = serializers.IntegerField(read_only=True) utilization = serializers.CharField(read_only=True) + # Related object counts + vlan_count = RelatedObjectCountField('ipam.vlan', 'group') + class Meta: model = VLANGroup fields = [ @@ -247,7 +257,9 @@ class VLANSerializer(NetBoxModelSerializer): status = ChoiceField(choices=VLANStatusChoices, required=False) role = NestedRoleSerializer(required=False, allow_null=True) l2vpn_termination = NestedL2VPNTerminationSerializer(read_only=True, allow_null=True) - prefix_count = serializers.IntegerField(read_only=True) + + # Related object counts + prefix_count = RelatedObjectCountField('ipam.prefix', 'vlan') class Meta: model = VLAN diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index c3594bdcf..d928e6a26 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -12,8 +12,6 @@ from rest_framework.response import Response from rest_framework.routers import APIRootView from rest_framework.views import APIView -from circuits.models import Provider -from dcim.models import Site from ipam import filtersets from ipam.models import * from ipam.utils import get_next_available_prefix @@ -22,7 +20,6 @@ from netbox.api.viewsets.mixins import ObjectValidationMixin from netbox.config import get_config from netbox.constants import ADVISORY_LOCK_KEYS from utilities.api import get_serializer_for_model -from utilities.utils import count_related from . import serializers @@ -45,19 +42,13 @@ class ASNRangeViewSet(NetBoxModelViewSet): class ASNViewSet(NetBoxModelViewSet): - queryset = ASN.objects.annotate( - site_count=count_related(Site, 'asns'), - provider_count=count_related(Provider, 'asns') - ) + queryset = ASN.objects.all() serializer_class = serializers.ASNSerializer filterset_class = filtersets.ASNFilterSet class VRFViewSet(NetBoxModelViewSet): - queryset = VRF.objects.annotate( - ipaddress_count=count_related(IPAddress, 'vrf'), - prefix_count=count_related(Prefix, 'vrf') - ) + queryset = VRF.objects.all() serializer_class = serializers.VRFSerializer filterset_class = filtersets.VRFFilterSet @@ -69,9 +60,7 @@ class RouteTargetViewSet(NetBoxModelViewSet): class RIRViewSet(NetBoxModelViewSet): - queryset = RIR.objects.annotate( - aggregate_count=count_related(Aggregate, 'rir') - ) + queryset = RIR.objects.all() serializer_class = serializers.RIRSerializer filterset_class = filtersets.RIRFilterSet @@ -83,10 +72,7 @@ class AggregateViewSet(NetBoxModelViewSet): class RoleViewSet(NetBoxModelViewSet): - queryset = Role.objects.annotate( - prefix_count=count_related(Prefix, 'role'), - vlan_count=count_related(VLAN, 'role') - ) + queryset = Role.objects.all() serializer_class = serializers.RoleSerializer filterset_class = filtersets.RoleFilterSet @@ -151,8 +137,6 @@ class VLANGroupViewSet(NetBoxModelViewSet): class VLANViewSet(NetBoxModelViewSet): queryset = VLAN.objects.prefetch_related( 'l2vpn_terminations', # Referenced by VLANSerializer.l2vpn_termination - ).annotate( - prefix_count=count_related(Prefix, 'vlan') ) serializer_class = serializers.VLANSerializer filterset_class = filtersets.VLANFilterSet diff --git a/netbox/tenancy/api/serializers.py b/netbox/tenancy/api/serializers.py index 118cafd81..48d018742 100644 --- a/netbox/tenancy/api/serializers.py +++ b/netbox/tenancy/api/serializers.py @@ -3,7 +3,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema_field from rest_framework import serializers -from netbox.api.fields import ChoiceField, ContentTypeField +from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer from netbox.constants import NESTED_SERIALIZER_PREFIX from tenancy.choices import ContactPriorityChoices @@ -32,16 +32,18 @@ class TenantGroupSerializer(NestedGroupModelSerializer): class TenantSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail') group = NestedTenantGroupSerializer(required=False, allow_null=True) - circuit_count = serializers.IntegerField(read_only=True) - device_count = serializers.IntegerField(read_only=True) - ipaddress_count = serializers.IntegerField(read_only=True) - prefix_count = serializers.IntegerField(read_only=True) - rack_count = serializers.IntegerField(read_only=True) - site_count = serializers.IntegerField(read_only=True) - virtualmachine_count = serializers.IntegerField(read_only=True) - vlan_count = serializers.IntegerField(read_only=True) - vrf_count = serializers.IntegerField(read_only=True) - cluster_count = serializers.IntegerField(read_only=True) + + # Related object counts + circuit_count = RelatedObjectCountField('circuits.circuit', 'tenant') + device_count = RelatedObjectCountField('dcim.device', 'tenant') + rack_count = RelatedObjectCountField('dcim.rack', 'tenant') + site_count = RelatedObjectCountField('dcim.site', 'tenant') + ipaddress_count = RelatedObjectCountField('ipam.ipaddress', 'tenant') + prefix_count = RelatedObjectCountField('ipam.prefix', 'tenant') + vlan_count = RelatedObjectCountField('ipam.vlan', 'tenant') + vrf_count = RelatedObjectCountField('ipam.vrf', 'tenant') + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'tenant') + cluster_count = RelatedObjectCountField('virtualization.cluster', 'tenant') class Meta: model = Tenant diff --git a/netbox/tenancy/api/views.py b/netbox/tenancy/api/views.py index b922935ae..70330ddb8 100644 --- a/netbox/tenancy/api/views.py +++ b/netbox/tenancy/api/views.py @@ -1,13 +1,8 @@ from rest_framework.routers import APIRootView -from circuits.models import Circuit -from dcim.models import Device, Rack, Site -from ipam.models import IPAddress, Prefix, VLAN, VRF from netbox.api.viewsets import NetBoxModelViewSet, MPTTLockedMixin from tenancy import filtersets from tenancy.models import * -from utilities.utils import count_related -from virtualization.models import VirtualMachine, Cluster from . import serializers @@ -36,18 +31,7 @@ class TenantGroupViewSet(MPTTLockedMixin, NetBoxModelViewSet): class TenantViewSet(NetBoxModelViewSet): - queryset = Tenant.objects.annotate( - circuit_count=count_related(Circuit, 'tenant'), - device_count=count_related(Device, 'tenant'), - ipaddress_count=count_related(IPAddress, 'tenant'), - prefix_count=count_related(Prefix, 'tenant'), - rack_count=count_related(Rack, 'tenant'), - site_count=count_related(Site, 'tenant'), - virtualmachine_count=count_related(VirtualMachine, 'tenant'), - vlan_count=count_related(VLAN, 'tenant'), - vrf_count=count_related(VRF, 'tenant'), - cluster_count=count_related(Cluster, 'tenant') - ) + queryset = Tenant.objects.all() serializer_class = serializers.TenantSerializer filterset_class = filtersets.TenantFilterSet diff --git a/netbox/virtualization/api/nested_serializers.py b/netbox/virtualization/api/nested_serializers.py index afb7e39a1..620df31d2 100644 --- a/netbox/virtualization/api/nested_serializers.py +++ b/netbox/virtualization/api/nested_serializers.py @@ -1,6 +1,7 @@ from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers +from netbox.api.fields import RelatedObjectCountField from netbox.api.serializers import WritableNestedSerializer from virtualization.models import * @@ -23,7 +24,7 @@ __all__ = [ ) class NestedClusterTypeSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail') - cluster_count = serializers.IntegerField(read_only=True) + cluster_count = RelatedObjectCountField('virtualization.cluster', 'type') class Meta: model = ClusterType @@ -35,7 +36,7 @@ class NestedClusterTypeSerializer(WritableNestedSerializer): ) class NestedClusterGroupSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail') - cluster_count = serializers.IntegerField(read_only=True) + cluster_count = RelatedObjectCountField('virtualization.cluster', 'group') class Meta: model = ClusterGroup @@ -47,7 +48,7 @@ class NestedClusterGroupSerializer(WritableNestedSerializer): ) class NestedClusterSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail') - virtualmachine_count = serializers.IntegerField(read_only=True) + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'cluster') class Meta: model = Cluster diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index 7ed36388b..991bcd714 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -8,7 +8,7 @@ from dcim.choices import InterfaceModeChoices from extras.api.nested_serializers import NestedConfigTemplateSerializer from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer from ipam.models import VLAN -from netbox.api.fields import ChoiceField, SerializedPKRelatedField +from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField from netbox.api.serializers import NetBoxModelSerializer from tenancy.api.nested_serializers import NestedTenantSerializer from virtualization.choices import * @@ -23,7 +23,9 @@ from .nested_serializers import * class ClusterTypeSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail') - cluster_count = serializers.IntegerField(read_only=True) + + # Related object counts + cluster_count = RelatedObjectCountField('virtualization.cluster', 'type') class Meta: model = ClusterType @@ -35,7 +37,9 @@ class ClusterTypeSerializer(NetBoxModelSerializer): class ClusterGroupSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail') - cluster_count = serializers.IntegerField(read_only=True) + + # Related object counts + cluster_count = RelatedObjectCountField('virtualization.cluster', 'group') class Meta: model = ClusterGroup @@ -52,8 +56,10 @@ class ClusterSerializer(NetBoxModelSerializer): status = ChoiceField(choices=ClusterStatusChoices, required=False) tenant = NestedTenantSerializer(required=False, allow_null=True) site = NestedSiteSerializer(required=False, allow_null=True, default=None) - device_count = serializers.IntegerField(read_only=True) - virtualmachine_count = serializers.IntegerField(read_only=True) + + # Related object counts + device_count = RelatedObjectCountField('dcim.device', 'cluster') + virtualmachine_count = RelatedObjectCountField('virtualization.virtualmachine', 'cluster') class Meta: model = Cluster diff --git a/netbox/virtualization/api/views.py b/netbox/virtualization/api/views.py index ff894c6dc..c9ed0f970 100644 --- a/netbox/virtualization/api/views.py +++ b/netbox/virtualization/api/views.py @@ -1,10 +1,8 @@ from rest_framework.routers import APIRootView -from dcim.models import Device from extras.api.mixins import ConfigContextQuerySetMixin, RenderConfigMixin from netbox.api.viewsets import NetBoxModelViewSet from utilities.query_functions import CollateAsChar -from utilities.utils import count_related from virtualization import filtersets from virtualization.models import * from . import serializers @@ -23,26 +21,19 @@ class VirtualizationRootView(APIRootView): # class ClusterTypeViewSet(NetBoxModelViewSet): - queryset = ClusterType.objects.annotate( - cluster_count=count_related(Cluster, 'type') - ) + queryset = ClusterType.objects.all() serializer_class = serializers.ClusterTypeSerializer filterset_class = filtersets.ClusterTypeFilterSet class ClusterGroupViewSet(NetBoxModelViewSet): - queryset = ClusterGroup.objects.annotate( - cluster_count=count_related(Cluster, 'group') - ) + queryset = ClusterGroup.objects.all() serializer_class = serializers.ClusterGroupSerializer filterset_class = filtersets.ClusterGroupFilterSet class ClusterViewSet(NetBoxModelViewSet): - queryset = Cluster.objects.annotate( - device_count=count_related(Device, 'cluster'), - virtualmachine_count=count_related(VirtualMachine, 'cluster') - ) + queryset = Cluster.objects.all() serializer_class = serializers.ClusterSerializer filterset_class = filtersets.ClusterFilterSet diff --git a/netbox/vpn/api/nested_serializers.py b/netbox/vpn/api/nested_serializers.py index 1042b375e..74f9f94b8 100644 --- a/netbox/vpn/api/nested_serializers.py +++ b/netbox/vpn/api/nested_serializers.py @@ -1,6 +1,7 @@ from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers +from netbox.api.fields import RelatedObjectCountField from netbox.api.serializers import WritableNestedSerializer from vpn import models @@ -23,7 +24,7 @@ __all__ = ( ) class NestedTunnelGroupSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail') - tunnel_count = serializers.IntegerField(read_only=True) + tunnel_count = RelatedObjectCountField('vpn.tunnel', 'group') class Meta: model = models.TunnelGroup diff --git a/netbox/vpn/api/serializers.py b/netbox/vpn/api/serializers.py index dedcbfbf5..cba83d4ed 100644 --- a/netbox/vpn/api/serializers.py +++ b/netbox/vpn/api/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedRouteTargetSerializer from ipam.models import RouteTarget -from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField +from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField from netbox.api.serializers import NetBoxModelSerializer from netbox.constants import NESTED_SERIALIZER_PREFIX from tenancy.api.nested_serializers import NestedTenantSerializer @@ -29,7 +29,9 @@ __all__ = ( class TunnelGroupSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail') - tunnel_count = serializers.IntegerField(read_only=True) + + # Related object counts + tunnel_count = RelatedObjectCountField('vpn.tunnel', 'group') class Meta: model = TunnelGroup @@ -59,11 +61,14 @@ class TunnelSerializer(NetBoxModelSerializer): allow_null=True ) + # Related object counts + terminations_count = RelatedObjectCountField('vpn.tunneltermination', 'tunnel') + class Meta: model = Tunnel fields = ( 'id', 'url', 'display', 'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id', - 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'terminations_count', ) diff --git a/netbox/vpn/api/views.py b/netbox/vpn/api/views.py index 5015f0618..4119d396a 100644 --- a/netbox/vpn/api/views.py +++ b/netbox/vpn/api/views.py @@ -1,7 +1,6 @@ from rest_framework.routers import APIRootView from netbox.api.viewsets import NetBoxModelViewSet -from utilities.utils import count_related from vpn import filtersets from vpn.models import * from . import serializers @@ -34,17 +33,13 @@ class VPNRootView(APIRootView): # class TunnelGroupViewSet(NetBoxModelViewSet): - queryset = TunnelGroup.objects.annotate( - tunnel_count=count_related(Tunnel, 'group') - ) + queryset = TunnelGroup.objects.all() serializer_class = serializers.TunnelGroupSerializer filterset_class = filtersets.TunnelGroupFilterSet class TunnelViewSet(NetBoxModelViewSet): - queryset = Tunnel.objects.annotate( - terminations_count=count_related(TunnelTermination, 'tunnel') - ) + queryset = Tunnel.objects.all() serializer_class = serializers.TunnelSerializer filterset_class = filtersets.TunnelFilterSet