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

Closes #15131: Dynamic queryset annotations for REST API endpoints (#15152)

* Introduce RelatedObjectCountField

* Introduce get_annotations_for_serializer() and enable dynamic annotations

* Add RelatedObjectCountFields to serializers; remove static annotations from querysets

* Remove annotations cleanup logic from BriefModeMixin

* Annotate type for RelatedObjectCountField

* Remove redundant field on TagSerializer

* Add missing reverse relationship for power feeds to rack

* Refactor RelatedObjectCountField to take a single relationship name
This commit is contained in:
Jeremy Stretch
2024-02-15 14:49:27 -05:00
committed by GitHub
parent b3f25a400b
commit 7abb2b2ab5
27 changed files with 204 additions and 221 deletions

View File

@@ -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('prefixes')
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('aggregates')
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('prefixes')
vlan_count = RelatedObjectCountField('vlans')
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('vlans')
class Meta:
model = models.VLANGroup

View File

@@ -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('sites')
provider_count = RelatedObjectCountField('providers')
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('ip_addresses')
prefix_count = RelatedObjectCountField('prefixes')
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('aggregates')
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('prefixes')
vlan_count = RelatedObjectCountField('vlans')
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('vlans')
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('prefixes')
class Meta:
model = VLAN

View File

@@ -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