Skip to content

Commit

Permalink
Closes #15131: Dynamic queryset annotations for REST API endpoints (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
jeremystretch committed Feb 15, 2024
1 parent b3f25a4 commit 7abb2b2
Show file tree
Hide file tree
Showing 27 changed files with 204 additions and 221 deletions.
8 changes: 4 additions & 4 deletions netbox/circuits/api/nested_serializers.py
Original file line number Diff line number Diff line change
@@ -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__ = [
Expand Down Expand Up @@ -36,7 +36,7 @@ class Meta:
)
class NestedProviderSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
circuit_count = serializers.IntegerField(read_only=True)
circuit_count = RelatedObjectCountField('circuits')

class Meta:
model = Provider
Expand Down Expand Up @@ -64,7 +64,7 @@ class Meta:
)
class NestedCircuitTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
circuit_count = serializers.IntegerField(read_only=True)
circuit_count = RelatedObjectCountField('circuits')

class Meta:
model = CircuitType
Expand Down
12 changes: 7 additions & 5 deletions netbox/circuits/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
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 *
Expand All @@ -32,7 +32,7 @@ class ProviderSerializer(NetBoxModelSerializer):
)

# Related object counts
circuit_count = serializers.IntegerField(read_only=True)
circuit_count = RelatedObjectCountField('circuits')

class Meta:
model = Provider
Expand Down Expand Up @@ -80,13 +80,15 @@ class Meta:

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')

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',
]


Expand Down
9 changes: 2 additions & 7 deletions netbox/circuits/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
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


Expand All @@ -21,9 +20,7 @@ def get_view_name(self):
#

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

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

Expand Down
6 changes: 2 additions & 4 deletions netbox/core/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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
Expand All @@ -28,9 +28,7 @@ class DataSourceSerializer(NetBoxModelSerializer):
)

# Related object counts
file_count = serializers.IntegerField(
read_only=True
)
file_count = RelatedObjectCountField('datafiles')

class Meta:
model = DataSource
Expand Down
5 changes: 1 addition & 4 deletions netbox/core/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from core import filtersets
from core.models import *
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
from utilities.utils import count_related
from . import serializers


Expand All @@ -22,9 +21,7 @@ def get_view_name(self):


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

Expand Down
24 changes: 12 additions & 12 deletions netbox/dcim/api/nested_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
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',
Expand Down Expand Up @@ -110,7 +111,7 @@ class Meta:
)
class NestedRackRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
rack_count = serializers.IntegerField(read_only=True)
rack_count = RelatedObjectCountField('racks')

class Meta:
model = models.RackRole
Expand All @@ -122,7 +123,7 @@ class Meta:
)
class NestedRackSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
device_count = serializers.IntegerField(read_only=True)
device_count = RelatedObjectCountField('devices')

class Meta:
model = models.Rack
Expand Down Expand Up @@ -150,7 +151,7 @@ def get_user(self, obj):
)
class NestedManufacturerSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
devicetype_count = serializers.IntegerField(read_only=True)
devicetype_count = RelatedObjectCountField('device_types')

class Meta:
model = models.Manufacturer
Expand All @@ -163,7 +164,7 @@ class Meta:
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('instances')

class Meta:
model = models.DeviceType
Expand All @@ -173,7 +174,6 @@ class Meta:
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
Expand Down Expand Up @@ -274,8 +274,8 @@ class Meta:
)
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('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')

class Meta:
model = models.DeviceRole
Expand All @@ -287,8 +287,8 @@ class Meta:
)
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('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')

class Meta:
model = models.Platform
Expand Down Expand Up @@ -445,7 +445,7 @@ class Meta:
)
class NestedInventoryItemRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
inventoryitem_count = serializers.IntegerField(read_only=True)
inventoryitem_count = RelatedObjectCountField('inventory_items')

class Meta:
model = models.InventoryItemRole
Expand Down Expand Up @@ -490,7 +490,7 @@ class Meta:
)
class NestedPowerPanelSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
powerfeed_count = serializers.IntegerField(read_only=True)
powerfeed_count = RelatedObjectCountField('powerfeeds')

class Meta:
model = models.PowerPanel
Expand Down
58 changes: 37 additions & 21 deletions netbox/dcim/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
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,
Expand Down Expand Up @@ -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('circuit_terminations')
device_count = RelatedObjectCountField('devices')
prefix_count = RelatedObjectCountField('prefixes')
rack_count = RelatedObjectCountField('racks')
vlan_count = RelatedObjectCountField('vlans')
virtualmachine_count = RelatedObjectCountField('virtual_machines')

class Meta:
model = Site
Expand Down Expand Up @@ -184,7 +184,9 @@ class Meta:

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('racks')

class Meta:
model = RackRole
Expand All @@ -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('devices')
powerfeed_count = RelatedObjectCountField('powerfeeds')

class Meta:
model = Rack
Expand Down Expand Up @@ -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('device_types')
inventoryitem_count = RelatedObjectCountField('inventory_items')
platform_count = RelatedObjectCountField('platforms')

class Meta:
model = Manufacturer
Expand All @@ -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)
Expand All @@ -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('instances')

class Meta:
model = DeviceType
fields = [
Expand Down Expand Up @@ -636,8 +644,10 @@ def get_component(self, obj):
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('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')

class Meta:
model = DeviceRole
Expand All @@ -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('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')

class Meta:
model = Platform
Expand Down Expand Up @@ -761,7 +773,7 @@ class VirtualDeviceContextSerializer(NetBoxModelSerializer):
status = ChoiceField(choices=VirtualDeviceContextStatusChoices)

# Related object counts
interface_count = serializers.IntegerField(read_only=True)
interface_count = RelatedObjectCountField('interfaces')

class Meta:
model = VirtualDeviceContext
Expand Down Expand Up @@ -1092,7 +1104,9 @@ def get_component(self, obj):

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('inventory_items')

class Meta:
model = InventoryItemRole
Expand Down Expand Up @@ -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('powerfeeds')

class Meta:
model = PowerPanel
Expand Down
Loading

0 comments on commit 7abb2b2

Please sign in to comment.