mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* 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:
@ -1,8 +1,8 @@
|
|||||||
from drf_spectacular.utils import extend_schema_field, extend_schema_serializer
|
from drf_spectacular.utils import extend_schema_serializer
|
||||||
from drf_spectacular.types import OpenApiTypes
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
|
from netbox.api.fields import RelatedObjectCountField
|
||||||
from netbox.api.serializers import WritableNestedSerializer
|
from netbox.api.serializers import WritableNestedSerializer
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -36,7 +36,7 @@ class NestedProviderNetworkSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedProviderSerializer(WritableNestedSerializer):
|
class NestedProviderSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
||||||
circuit_count = serializers.IntegerField(read_only=True)
|
circuit_count = RelatedObjectCountField('circuits')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Provider
|
model = Provider
|
||||||
@ -64,7 +64,7 @@ class NestedProviderAccountSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedCircuitTypeSerializer(WritableNestedSerializer):
|
class NestedCircuitTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
||||||
circuit_count = serializers.IntegerField(read_only=True)
|
circuit_count = RelatedObjectCountField('circuits')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitType
|
model = CircuitType
|
||||||
|
@ -6,7 +6,7 @@ from dcim.api.nested_serializers import NestedSiteSerializer
|
|||||||
from dcim.api.serializers import CabledObjectSerializer
|
from dcim.api.serializers import CabledObjectSerializer
|
||||||
from ipam.models import ASN
|
from ipam.models import ASN
|
||||||
from ipam.api.nested_serializers import NestedASNSerializer
|
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 netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
|
||||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||||
from .nested_serializers import *
|
from .nested_serializers import *
|
||||||
@ -32,7 +32,7 @@ class ProviderSerializer(NetBoxModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
circuit_count = serializers.IntegerField(read_only=True)
|
circuit_count = RelatedObjectCountField('circuits')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Provider
|
model = Provider
|
||||||
@ -80,13 +80,15 @@ class ProviderNetworkSerializer(NetBoxModelSerializer):
|
|||||||
|
|
||||||
class CircuitTypeSerializer(NetBoxModelSerializer):
|
class CircuitTypeSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
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:
|
class Meta:
|
||||||
model = CircuitType
|
model = CircuitType
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
|
||||||
'circuit_count',
|
'last_updated', 'circuit_count',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ from circuits import filtersets
|
|||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from dcim.api.views import PassThroughPortMixin
|
from dcim.api.views import PassThroughPortMixin
|
||||||
from netbox.api.viewsets import NetBoxModelViewSet
|
from netbox.api.viewsets import NetBoxModelViewSet
|
||||||
from utilities.utils import count_related
|
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
@ -21,9 +20,7 @@ class CircuitsRootView(APIRootView):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class ProviderViewSet(NetBoxModelViewSet):
|
class ProviderViewSet(NetBoxModelViewSet):
|
||||||
queryset = Provider.objects.annotate(
|
queryset = Provider.objects.all()
|
||||||
circuit_count=count_related(Circuit, 'provider')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.ProviderSerializer
|
serializer_class = serializers.ProviderSerializer
|
||||||
filterset_class = filtersets.ProviderFilterSet
|
filterset_class = filtersets.ProviderFilterSet
|
||||||
|
|
||||||
@ -33,9 +30,7 @@ class ProviderViewSet(NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class CircuitTypeViewSet(NetBoxModelViewSet):
|
class CircuitTypeViewSet(NetBoxModelViewSet):
|
||||||
queryset = CircuitType.objects.annotate(
|
queryset = CircuitType.objects.all()
|
||||||
circuit_count=count_related(Circuit, 'type')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.CircuitTypeSerializer
|
serializer_class = serializers.CircuitTypeSerializer
|
||||||
filterset_class = filtersets.CircuitTypeFilterSet
|
filterset_class = filtersets.CircuitTypeFilterSet
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ from rest_framework import serializers
|
|||||||
|
|
||||||
from core.choices import *
|
from core.choices import *
|
||||||
from core.models 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.api.serializers import BaseModelSerializer, NetBoxModelSerializer
|
||||||
from netbox.utils import get_data_backend_choices
|
from netbox.utils import get_data_backend_choices
|
||||||
from users.api.nested_serializers import NestedUserSerializer
|
from users.api.nested_serializers import NestedUserSerializer
|
||||||
@ -28,9 +28,7 @@ class DataSourceSerializer(NetBoxModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
file_count = serializers.IntegerField(
|
file_count = RelatedObjectCountField('datafiles')
|
||||||
read_only=True
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DataSource
|
model = DataSource
|
||||||
|
@ -9,7 +9,6 @@ from rest_framework.viewsets import ReadOnlyModelViewSet
|
|||||||
from core import filtersets
|
from core import filtersets
|
||||||
from core.models import *
|
from core.models import *
|
||||||
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
|
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
|
||||||
from utilities.utils import count_related
|
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
@ -22,9 +21,7 @@ class CoreRootView(APIRootView):
|
|||||||
|
|
||||||
|
|
||||||
class DataSourceViewSet(NetBoxModelViewSet):
|
class DataSourceViewSet(NetBoxModelViewSet):
|
||||||
queryset = DataSource.objects.annotate(
|
queryset = DataSource.objects.all()
|
||||||
file_count=count_related(DataFile, 'source')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.DataSourceSerializer
|
serializer_class = serializers.DataSourceSerializer
|
||||||
filterset_class = filtersets.DataSourceFilterSet
|
filterset_class = filtersets.DataSourceFilterSet
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@ from drf_spectacular.utils import extend_schema_serializer
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from dcim import models
|
from dcim import models
|
||||||
from netbox.api.serializers import BaseModelSerializer, WritableNestedSerializer
|
from netbox.api.fields import RelatedObjectCountField
|
||||||
|
from netbox.api.serializers import WritableNestedSerializer
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ComponentNestedModuleSerializer',
|
'ComponentNestedModuleSerializer',
|
||||||
@ -110,7 +111,7 @@ class NestedLocationSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedRackRoleSerializer(WritableNestedSerializer):
|
class NestedRackRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
||||||
rack_count = serializers.IntegerField(read_only=True)
|
rack_count = RelatedObjectCountField('racks')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.RackRole
|
model = models.RackRole
|
||||||
@ -122,7 +123,7 @@ class NestedRackRoleSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedRackSerializer(WritableNestedSerializer):
|
class NestedRackSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = RelatedObjectCountField('devices')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Rack
|
model = models.Rack
|
||||||
@ -150,7 +151,7 @@ class NestedRackReservationSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedManufacturerSerializer(WritableNestedSerializer):
|
class NestedManufacturerSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
||||||
devicetype_count = serializers.IntegerField(read_only=True)
|
devicetype_count = RelatedObjectCountField('device_types')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Manufacturer
|
model = models.Manufacturer
|
||||||
@ -163,7 +164,7 @@ class NestedManufacturerSerializer(WritableNestedSerializer):
|
|||||||
class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
||||||
manufacturer = NestedManufacturerSerializer(read_only=True)
|
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = RelatedObjectCountField('instances')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.DeviceType
|
model = models.DeviceType
|
||||||
@ -173,7 +174,6 @@ class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
|||||||
class NestedModuleTypeSerializer(WritableNestedSerializer):
|
class NestedModuleTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
|
||||||
manufacturer = NestedManufacturerSerializer(read_only=True)
|
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||||
# module_count = serializers.IntegerField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ModuleType
|
model = models.ModuleType
|
||||||
@ -274,8 +274,8 @@ class NestedInventoryItemTemplateSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = RelatedObjectCountField('devices')
|
||||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.DeviceRole
|
model = models.DeviceRole
|
||||||
@ -287,8 +287,8 @@ class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedPlatformSerializer(WritableNestedSerializer):
|
class NestedPlatformSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = RelatedObjectCountField('devices')
|
||||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Platform
|
model = models.Platform
|
||||||
@ -445,7 +445,7 @@ class NestedInventoryItemSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedInventoryItemRoleSerializer(WritableNestedSerializer):
|
class NestedInventoryItemRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
|
||||||
inventoryitem_count = serializers.IntegerField(read_only=True)
|
inventoryitem_count = RelatedObjectCountField('inventory_items')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.InventoryItemRole
|
model = models.InventoryItemRole
|
||||||
@ -490,7 +490,7 @@ class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedPowerPanelSerializer(WritableNestedSerializer):
|
class NestedPowerPanelSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
|
||||||
powerfeed_count = serializers.IntegerField(read_only=True)
|
powerfeed_count = RelatedObjectCountField('powerfeeds')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.PowerPanel
|
model = models.PowerPanel
|
||||||
|
@ -15,7 +15,7 @@ from ipam.api.nested_serializers import (
|
|||||||
NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer,
|
NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer,
|
||||||
)
|
)
|
||||||
from ipam.models import ASN, VLAN
|
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 (
|
from netbox.api.serializers import (
|
||||||
GenericObjectSerializer, NestedGroupModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer,
|
GenericObjectSerializer, NestedGroupModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer,
|
||||||
WritableNestedSerializer,
|
WritableNestedSerializer,
|
||||||
@ -144,12 +144,12 @@ class SiteSerializer(NetBoxModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
circuit_count = serializers.IntegerField(read_only=True)
|
circuit_count = RelatedObjectCountField('circuit_terminations')
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
device_count = RelatedObjectCountField('devices')
|
||||||
prefix_count = serializers.IntegerField(read_only=True)
|
prefix_count = RelatedObjectCountField('prefixes')
|
||||||
rack_count = serializers.IntegerField(read_only=True)
|
rack_count = RelatedObjectCountField('racks')
|
||||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
vlan_count = RelatedObjectCountField('vlans')
|
||||||
vlan_count = serializers.IntegerField(read_only=True)
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Site
|
model = Site
|
||||||
@ -184,7 +184,9 @@ class LocationSerializer(NestedGroupModelSerializer):
|
|||||||
|
|
||||||
class RackRoleSerializer(NetBoxModelSerializer):
|
class RackRoleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
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:
|
class Meta:
|
||||||
model = RackRole
|
model = RackRole
|
||||||
@ -207,8 +209,10 @@ class RackSerializer(NetBoxModelSerializer):
|
|||||||
width = ChoiceField(choices=RackWidthChoices, required=False)
|
width = ChoiceField(choices=RackWidthChoices, required=False)
|
||||||
outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False, allow_null=True)
|
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)
|
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:
|
class Meta:
|
||||||
model = Rack
|
model = Rack
|
||||||
@ -299,9 +303,11 @@ class RackElevationDetailFilterSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
class ManufacturerSerializer(NetBoxModelSerializer):
|
class ManufacturerSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
||||||
devicetype_count = serializers.IntegerField(read_only=True)
|
|
||||||
inventoryitem_count = serializers.IntegerField(read_only=True)
|
# Related object counts
|
||||||
platform_count = serializers.IntegerField(read_only=True)
|
devicetype_count = RelatedObjectCountField('device_types')
|
||||||
|
inventoryitem_count = RelatedObjectCountField('inventory_items')
|
||||||
|
platform_count = RelatedObjectCountField('platforms')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
@ -325,7 +331,6 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
|
|||||||
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False, allow_null=True)
|
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)
|
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)
|
weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
|
||||||
|
|
||||||
# Counter fields
|
# Counter fields
|
||||||
console_port_template_count = serializers.IntegerField(read_only=True)
|
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)
|
module_bay_template_count = serializers.IntegerField(read_only=True)
|
||||||
inventory_item_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:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = [
|
fields = [
|
||||||
@ -636,8 +644,10 @@ class InventoryItemTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class DeviceRoleSerializer(NetBoxModelSerializer):
|
class DeviceRoleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
||||||
config_template = NestedConfigTemplateSerializer(required=False, allow_null=True, default=None)
|
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:
|
class Meta:
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
@ -651,8 +661,10 @@ class PlatformSerializer(NetBoxModelSerializer):
|
|||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
||||||
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
|
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
|
||||||
config_template = NestedConfigTemplateSerializer(required=False, allow_null=True, default=None)
|
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:
|
class Meta:
|
||||||
model = Platform
|
model = Platform
|
||||||
@ -761,7 +773,7 @@ class VirtualDeviceContextSerializer(NetBoxModelSerializer):
|
|||||||
status = ChoiceField(choices=VirtualDeviceContextStatusChoices)
|
status = ChoiceField(choices=VirtualDeviceContextStatusChoices)
|
||||||
|
|
||||||
# Related object counts
|
# Related object counts
|
||||||
interface_count = serializers.IntegerField(read_only=True)
|
interface_count = RelatedObjectCountField('interfaces')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VirtualDeviceContext
|
model = VirtualDeviceContext
|
||||||
@ -1092,7 +1104,9 @@ class InventoryItemSerializer(NetBoxModelSerializer):
|
|||||||
|
|
||||||
class InventoryItemRoleSerializer(NetBoxModelSerializer):
|
class InventoryItemRoleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
|
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:
|
class Meta:
|
||||||
model = InventoryItemRole
|
model = InventoryItemRole
|
||||||
@ -1204,7 +1218,9 @@ class PowerPanelSerializer(NetBoxModelSerializer):
|
|||||||
allow_null=True,
|
allow_null=True,
|
||||||
default=None
|
default=None
|
||||||
)
|
)
|
||||||
powerfeed_count = serializers.IntegerField(read_only=True)
|
|
||||||
|
# Related object counts
|
||||||
|
powerfeed_count = RelatedObjectCountField('powerfeeds')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PowerPanel
|
model = PowerPanel
|
||||||
|
@ -13,7 +13,6 @@ from dcim.constants import CABLE_TRACE_SVG_DEFAULT_WIDTH
|
|||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from dcim.svg import CableTraceSVG
|
from dcim.svg import CableTraceSVG
|
||||||
from extras.api.mixins import ConfigContextQuerySetMixin, RenderConfigMixin
|
from extras.api.mixins import ConfigContextQuerySetMixin, RenderConfigMixin
|
||||||
from ipam.models import Prefix, VLAN
|
|
||||||
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
|
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
|
||||||
from netbox.api.metadata import ContentTypeMetadata
|
from netbox.api.metadata import ContentTypeMetadata
|
||||||
from netbox.api.pagination import StripCountAnnotationsPaginator
|
from netbox.api.pagination import StripCountAnnotationsPaginator
|
||||||
@ -23,7 +22,6 @@ from netbox.constants import NESTED_SERIALIZER_PREFIX
|
|||||||
from utilities.api import get_serializer_for_model
|
from utilities.api import get_serializer_for_model
|
||||||
from utilities.query_functions import CollateAsChar
|
from utilities.query_functions import CollateAsChar
|
||||||
from utilities.utils import count_related
|
from utilities.utils import count_related
|
||||||
from virtualization.models import VirtualMachine
|
|
||||||
from . import serializers
|
from . import serializers
|
||||||
from .exceptions import MissingFilterException
|
from .exceptions import MissingFilterException
|
||||||
|
|
||||||
@ -129,14 +127,7 @@ class SiteGroupViewSet(MPTTLockedMixin, NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class SiteViewSet(NetBoxModelViewSet):
|
class SiteViewSet(NetBoxModelViewSet):
|
||||||
queryset = Site.objects.annotate(
|
queryset = Site.objects.all()
|
||||||
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')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.SiteSerializer
|
serializer_class = serializers.SiteSerializer
|
||||||
filterset_class = filtersets.SiteFilterSet
|
filterset_class = filtersets.SiteFilterSet
|
||||||
|
|
||||||
@ -168,9 +159,7 @@ class LocationViewSet(MPTTLockedMixin, NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class RackRoleViewSet(NetBoxModelViewSet):
|
class RackRoleViewSet(NetBoxModelViewSet):
|
||||||
queryset = RackRole.objects.annotate(
|
queryset = RackRole.objects.all()
|
||||||
rack_count=count_related(Rack, 'role')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.RackRoleSerializer
|
serializer_class = serializers.RackRoleSerializer
|
||||||
filterset_class = filtersets.RackRoleFilterSet
|
filterset_class = filtersets.RackRoleFilterSet
|
||||||
|
|
||||||
@ -180,10 +169,7 @@ class RackRoleViewSet(NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class RackViewSet(NetBoxModelViewSet):
|
class RackViewSet(NetBoxModelViewSet):
|
||||||
queryset = Rack.objects.annotate(
|
queryset = Rack.objects.all()
|
||||||
device_count=count_related(Device, 'rack'),
|
|
||||||
powerfeed_count=count_related(PowerFeed, 'rack')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.RackSerializer
|
serializer_class = serializers.RackSerializer
|
||||||
filterset_class = filtersets.RackFilterSet
|
filterset_class = filtersets.RackFilterSet
|
||||||
|
|
||||||
@ -255,11 +241,7 @@ class RackReservationViewSet(NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class ManufacturerViewSet(NetBoxModelViewSet):
|
class ManufacturerViewSet(NetBoxModelViewSet):
|
||||||
queryset = Manufacturer.objects.annotate(
|
queryset = Manufacturer.objects.all()
|
||||||
devicetype_count=count_related(DeviceType, 'manufacturer'),
|
|
||||||
inventoryitem_count=count_related(InventoryItem, 'manufacturer'),
|
|
||||||
platform_count=count_related(Platform, 'manufacturer')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.ManufacturerSerializer
|
serializer_class = serializers.ManufacturerSerializer
|
||||||
filterset_class = filtersets.ManufacturerFilterSet
|
filterset_class = filtersets.ManufacturerFilterSet
|
||||||
|
|
||||||
@ -269,9 +251,7 @@ class ManufacturerViewSet(NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class DeviceTypeViewSet(NetBoxModelViewSet):
|
class DeviceTypeViewSet(NetBoxModelViewSet):
|
||||||
queryset = DeviceType.objects.annotate(
|
queryset = DeviceType.objects.all()
|
||||||
device_count=count_related(Device, 'device_type')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.DeviceTypeSerializer
|
serializer_class = serializers.DeviceTypeSerializer
|
||||||
filterset_class = filtersets.DeviceTypeFilterSet
|
filterset_class = filtersets.DeviceTypeFilterSet
|
||||||
|
|
||||||
@ -351,10 +331,7 @@ class InventoryItemTemplateViewSet(MPTTLockedMixin, NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class DeviceRoleViewSet(NetBoxModelViewSet):
|
class DeviceRoleViewSet(NetBoxModelViewSet):
|
||||||
queryset = DeviceRole.objects.annotate(
|
queryset = DeviceRole.objects.all()
|
||||||
device_count=count_related(Device, 'role'),
|
|
||||||
virtualmachine_count=count_related(VirtualMachine, 'role')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.DeviceRoleSerializer
|
serializer_class = serializers.DeviceRoleSerializer
|
||||||
filterset_class = filtersets.DeviceRoleFilterSet
|
filterset_class = filtersets.DeviceRoleFilterSet
|
||||||
|
|
||||||
@ -364,10 +341,7 @@ class DeviceRoleViewSet(NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class PlatformViewSet(NetBoxModelViewSet):
|
class PlatformViewSet(NetBoxModelViewSet):
|
||||||
queryset = Platform.objects.annotate(
|
queryset = Platform.objects.all()
|
||||||
device_count=count_related(Device, 'platform'),
|
|
||||||
virtualmachine_count=count_related(VirtualMachine, 'platform')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.PlatformSerializer
|
serializer_class = serializers.PlatformSerializer
|
||||||
filterset_class = filtersets.PlatformFilterSet
|
filterset_class = filtersets.PlatformFilterSet
|
||||||
|
|
||||||
@ -410,9 +384,7 @@ class DeviceViewSet(
|
|||||||
|
|
||||||
|
|
||||||
class VirtualDeviceContextViewSet(NetBoxModelViewSet):
|
class VirtualDeviceContextViewSet(NetBoxModelViewSet):
|
||||||
queryset = VirtualDeviceContext.objects.annotate(
|
queryset = VirtualDeviceContext.objects.all()
|
||||||
interface_count=count_related(Interface, 'vdcs'),
|
|
||||||
)
|
|
||||||
serializer_class = serializers.VirtualDeviceContextSerializer
|
serializer_class = serializers.VirtualDeviceContextSerializer
|
||||||
filterset_class = filtersets.VirtualDeviceContextFilterSet
|
filterset_class = filtersets.VirtualDeviceContextFilterSet
|
||||||
|
|
||||||
@ -513,9 +485,7 @@ class InventoryItemViewSet(MPTTLockedMixin, NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class InventoryItemRoleViewSet(NetBoxModelViewSet):
|
class InventoryItemRoleViewSet(NetBoxModelViewSet):
|
||||||
queryset = InventoryItemRole.objects.annotate(
|
queryset = InventoryItemRole.objects.all()
|
||||||
inventoryitem_count=count_related(InventoryItem, 'role')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.InventoryItemRoleSerializer
|
serializer_class = serializers.InventoryItemRoleSerializer
|
||||||
filterset_class = filtersets.InventoryItemRoleFilterSet
|
filterset_class = filtersets.InventoryItemRoleFilterSet
|
||||||
|
|
||||||
@ -552,9 +522,7 @@ class VirtualChassisViewSet(NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class PowerPanelViewSet(NetBoxModelViewSet):
|
class PowerPanelViewSet(NetBoxModelViewSet):
|
||||||
queryset = PowerPanel.objects.annotate(
|
queryset = PowerPanel.objects.all()
|
||||||
powerfeed_count=count_related(PowerFeed, 'power_panel')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.PowerPanelSerializer
|
serializer_class = serializers.PowerPanelSerializer
|
||||||
filterset_class = filtersets.PowerPanelFilterSet
|
filterset_class = filtersets.PowerPanelFilterSet
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='powerfeed',
|
model_name='powerfeed',
|
||||||
name='rack',
|
name='rack',
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.rack'),
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.rack'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='powerfeed',
|
model_name='powerfeed',
|
||||||
|
@ -84,6 +84,7 @@ class PowerFeed(PrimaryModel, PathEndpoint, CabledObjectModel):
|
|||||||
rack = models.ForeignKey(
|
rack = models.ForeignKey(
|
||||||
to='Rack',
|
to='Rack',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
|
related_name='powerfeeds',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,6 @@ from django.core.exceptions import ObjectDoesNotExist
|
|||||||
from drf_spectacular.types import OpenApiTypes
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import extend_schema_field
|
from drf_spectacular.utils import extend_schema_field
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.fields import ListField
|
|
||||||
|
|
||||||
from core.api.nested_serializers import NestedDataSourceSerializer, NestedDataFileSerializer, NestedJobSerializer
|
from core.api.nested_serializers import NestedDataSourceSerializer, NestedDataFileSerializer, NestedJobSerializer
|
||||||
from core.api.serializers import JobSerializer
|
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.choices import *
|
||||||
from extras.models import *
|
from extras.models import *
|
||||||
from netbox.api.exceptions import SerializerNotFound
|
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 import BaseModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer
|
||||||
from netbox.api.serializers.features import TaggableModelSerializer
|
from netbox.api.serializers.features import TaggableModelSerializer
|
||||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||||
@ -288,7 +287,9 @@ class TagSerializer(ValidatedModelSerializer):
|
|||||||
many=True,
|
many=True,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
tagged_items = serializers.IntegerField(read_only=True)
|
|
||||||
|
# Related object counts
|
||||||
|
tagged_items = RelatedObjectCountField('extras_taggeditem_items')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tag
|
model = Tag
|
||||||
|
@ -23,7 +23,7 @@ from netbox.api.metadata import ContentTypeMetadata
|
|||||||
from netbox.api.renderers import TextRenderer
|
from netbox.api.renderers import TextRenderer
|
||||||
from netbox.api.viewsets import NetBoxModelViewSet
|
from netbox.api.viewsets import NetBoxModelViewSet
|
||||||
from utilities.exceptions import RQWorkerNotRunningException
|
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 . import serializers
|
||||||
from .mixins import ConfigTemplateRenderMixin
|
from .mixins import ConfigTemplateRenderMixin
|
||||||
|
|
||||||
@ -147,9 +147,7 @@ class BookmarkViewSet(NetBoxModelViewSet):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class TagViewSet(NetBoxModelViewSet):
|
class TagViewSet(NetBoxModelViewSet):
|
||||||
queryset = Tag.objects.annotate(
|
queryset = Tag.objects.all()
|
||||||
tagged_items=count_related(TaggedItem, 'tag')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.TagSerializer
|
serializer_class = serializers.TagSerializer
|
||||||
filterset_class = filtersets.TagFilterSet
|
filterset_class = filtersets.TagFilterSet
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ from drf_spectacular.utils import extend_schema_serializer
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from ipam import models
|
from ipam import models
|
||||||
|
from netbox.api.fields import RelatedObjectCountField
|
||||||
from netbox.api.serializers import WritableNestedSerializer
|
from netbox.api.serializers import WritableNestedSerializer
|
||||||
from .field_serializers import IPAddressField
|
from .field_serializers import IPAddressField
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ class NestedASNSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedVRFSerializer(WritableNestedSerializer):
|
class NestedVRFSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
|
||||||
prefix_count = serializers.IntegerField(read_only=True)
|
prefix_count = RelatedObjectCountField('prefixes')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.VRF
|
model = models.VRF
|
||||||
@ -86,7 +87,7 @@ class NestedRouteTargetSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedRIRSerializer(WritableNestedSerializer):
|
class NestedRIRSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
|
||||||
aggregate_count = serializers.IntegerField(read_only=True)
|
aggregate_count = RelatedObjectCountField('aggregates')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.RIR
|
model = models.RIR
|
||||||
@ -132,8 +133,8 @@ class NestedFHRPGroupAssignmentSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedRoleSerializer(WritableNestedSerializer):
|
class NestedRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
|
||||||
prefix_count = serializers.IntegerField(read_only=True)
|
prefix_count = RelatedObjectCountField('prefixes')
|
||||||
vlan_count = serializers.IntegerField(read_only=True)
|
vlan_count = RelatedObjectCountField('vlans')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Role
|
model = models.Role
|
||||||
@ -145,7 +146,7 @@ class NestedRoleSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedVLANGroupSerializer(WritableNestedSerializer):
|
class NestedVLANGroupSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
|
||||||
vlan_count = serializers.IntegerField(read_only=True)
|
vlan_count = RelatedObjectCountField('vlans')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.VLANGroup
|
model = models.VLANGroup
|
||||||
|
@ -6,7 +6,7 @@ from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerial
|
|||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES
|
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES
|
||||||
from ipam.models import *
|
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.api.serializers import NetBoxModelSerializer
|
||||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||||
@ -43,8 +43,10 @@ class ASNSerializer(NetBoxModelSerializer):
|
|||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
|
||||||
rir = NestedRIRSerializer(required=False, allow_null=True)
|
rir = NestedRIRSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer(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:
|
class Meta:
|
||||||
model = ASN
|
model = ASN
|
||||||
@ -90,8 +92,10 @@ class VRFSerializer(NetBoxModelSerializer):
|
|||||||
required=False,
|
required=False,
|
||||||
many=True
|
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:
|
class Meta:
|
||||||
model = VRF
|
model = VRF
|
||||||
@ -124,7 +128,9 @@ class RouteTargetSerializer(NetBoxModelSerializer):
|
|||||||
|
|
||||||
class RIRSerializer(NetBoxModelSerializer):
|
class RIRSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
|
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:
|
class Meta:
|
||||||
model = RIR
|
model = RIR
|
||||||
@ -195,8 +201,10 @@ class FHRPGroupAssignmentSerializer(NetBoxModelSerializer):
|
|||||||
|
|
||||||
class RoleSerializer(NetBoxModelSerializer):
|
class RoleSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
|
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:
|
class Meta:
|
||||||
model = Role
|
model = Role
|
||||||
@ -218,9 +226,11 @@ class VLANGroupSerializer(NetBoxModelSerializer):
|
|||||||
)
|
)
|
||||||
scope_id = serializers.IntegerField(allow_null=True, required=False, default=None)
|
scope_id = serializers.IntegerField(allow_null=True, required=False, default=None)
|
||||||
scope = serializers.SerializerMethodField(read_only=True)
|
scope = serializers.SerializerMethodField(read_only=True)
|
||||||
vlan_count = serializers.IntegerField(read_only=True)
|
|
||||||
utilization = serializers.CharField(read_only=True)
|
utilization = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
# Related object counts
|
||||||
|
vlan_count = RelatedObjectCountField('vlans')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLANGroup
|
model = VLANGroup
|
||||||
fields = [
|
fields = [
|
||||||
@ -247,7 +257,9 @@ class VLANSerializer(NetBoxModelSerializer):
|
|||||||
status = ChoiceField(choices=VLANStatusChoices, required=False)
|
status = ChoiceField(choices=VLANStatusChoices, required=False)
|
||||||
role = NestedRoleSerializer(required=False, allow_null=True)
|
role = NestedRoleSerializer(required=False, allow_null=True)
|
||||||
l2vpn_termination = NestedL2VPNTerminationSerializer(read_only=True, 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:
|
class Meta:
|
||||||
model = VLAN
|
model = VLAN
|
||||||
|
@ -12,8 +12,6 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from circuits.models import Provider
|
|
||||||
from dcim.models import Site
|
|
||||||
from ipam import filtersets
|
from ipam import filtersets
|
||||||
from ipam.models import *
|
from ipam.models import *
|
||||||
from ipam.utils import get_next_available_prefix
|
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.config import get_config
|
||||||
from netbox.constants import ADVISORY_LOCK_KEYS
|
from netbox.constants import ADVISORY_LOCK_KEYS
|
||||||
from utilities.api import get_serializer_for_model
|
from utilities.api import get_serializer_for_model
|
||||||
from utilities.utils import count_related
|
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
@ -45,19 +42,13 @@ class ASNRangeViewSet(NetBoxModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class ASNViewSet(NetBoxModelViewSet):
|
class ASNViewSet(NetBoxModelViewSet):
|
||||||
queryset = ASN.objects.annotate(
|
queryset = ASN.objects.all()
|
||||||
site_count=count_related(Site, 'asns'),
|
|
||||||
provider_count=count_related(Provider, 'asns')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.ASNSerializer
|
serializer_class = serializers.ASNSerializer
|
||||||
filterset_class = filtersets.ASNFilterSet
|
filterset_class = filtersets.ASNFilterSet
|
||||||
|
|
||||||
|
|
||||||
class VRFViewSet(NetBoxModelViewSet):
|
class VRFViewSet(NetBoxModelViewSet):
|
||||||
queryset = VRF.objects.annotate(
|
queryset = VRF.objects.all()
|
||||||
ipaddress_count=count_related(IPAddress, 'vrf'),
|
|
||||||
prefix_count=count_related(Prefix, 'vrf')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.VRFSerializer
|
serializer_class = serializers.VRFSerializer
|
||||||
filterset_class = filtersets.VRFFilterSet
|
filterset_class = filtersets.VRFFilterSet
|
||||||
|
|
||||||
@ -69,9 +60,7 @@ class RouteTargetViewSet(NetBoxModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class RIRViewSet(NetBoxModelViewSet):
|
class RIRViewSet(NetBoxModelViewSet):
|
||||||
queryset = RIR.objects.annotate(
|
queryset = RIR.objects.all()
|
||||||
aggregate_count=count_related(Aggregate, 'rir')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.RIRSerializer
|
serializer_class = serializers.RIRSerializer
|
||||||
filterset_class = filtersets.RIRFilterSet
|
filterset_class = filtersets.RIRFilterSet
|
||||||
|
|
||||||
@ -83,10 +72,7 @@ class AggregateViewSet(NetBoxModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class RoleViewSet(NetBoxModelViewSet):
|
class RoleViewSet(NetBoxModelViewSet):
|
||||||
queryset = Role.objects.annotate(
|
queryset = Role.objects.all()
|
||||||
prefix_count=count_related(Prefix, 'role'),
|
|
||||||
vlan_count=count_related(VLAN, 'role')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.RoleSerializer
|
serializer_class = serializers.RoleSerializer
|
||||||
filterset_class = filtersets.RoleFilterSet
|
filterset_class = filtersets.RoleFilterSet
|
||||||
|
|
||||||
@ -151,8 +137,6 @@ class VLANGroupViewSet(NetBoxModelViewSet):
|
|||||||
class VLANViewSet(NetBoxModelViewSet):
|
class VLANViewSet(NetBoxModelViewSet):
|
||||||
queryset = VLAN.objects.prefetch_related(
|
queryset = VLAN.objects.prefetch_related(
|
||||||
'l2vpn_terminations', # Referenced by VLANSerializer.l2vpn_termination
|
'l2vpn_terminations', # Referenced by VLANSerializer.l2vpn_termination
|
||||||
).annotate(
|
|
||||||
prefix_count=count_related(Prefix, 'vlan')
|
|
||||||
)
|
)
|
||||||
serializer_class = serializers.VLANSerializer
|
serializer_class = serializers.VLANSerializer
|
||||||
filterset_class = filtersets.VLANFilterSet
|
filterset_class = filtersets.VLANFilterSet
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from drf_spectacular.utils import extend_schema_field
|
|
||||||
from drf_spectacular.types import OpenApiTypes
|
from drf_spectacular.types import OpenApiTypes
|
||||||
|
from drf_spectacular.utils import extend_schema_field
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
@ -10,6 +10,7 @@ __all__ = (
|
|||||||
'ChoiceField',
|
'ChoiceField',
|
||||||
'ContentTypeField',
|
'ContentTypeField',
|
||||||
'IPNetworkSerializer',
|
'IPNetworkSerializer',
|
||||||
|
'RelatedObjectCountField',
|
||||||
'SerializedPKRelatedField',
|
'SerializedPKRelatedField',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -135,3 +136,16 @@ class SerializedPKRelatedField(PrimaryKeyRelatedField):
|
|||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return self.serializer(value, context={'request': self.context['request']}).data
|
return self.serializer(value, context={'request': self.context['request']}).data
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_field(OpenApiTypes.INT64)
|
||||||
|
class RelatedObjectCountField(serializers.ReadOnlyField):
|
||||||
|
"""
|
||||||
|
Represents a read-only integer count of related objects (e.g. the number of racks assigned to a site). This field
|
||||||
|
is detected by get_annotations_for_serializer() when determining the annotations to be added to a queryset
|
||||||
|
depending on the serializer fields selected for inclusion in the response.
|
||||||
|
"""
|
||||||
|
def __init__(self, relation, **kwargs):
|
||||||
|
self.relation = relation
|
||||||
|
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
@ -10,7 +10,7 @@ from rest_framework import mixins as drf_mixins
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
|
||||||
from utilities.api import get_prefetches_for_serializer
|
from utilities.api import get_annotations_for_serializer, get_prefetches_for_serializer
|
||||||
from utilities.exceptions import AbortRequest
|
from utilities.exceptions import AbortRequest
|
||||||
from . import mixins
|
from . import mixins
|
||||||
|
|
||||||
@ -44,15 +44,16 @@ class BaseViewSet(GenericViewSet):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = super().get_queryset()
|
qs = super().get_queryset()
|
||||||
|
serializer_class = self.get_serializer_class()
|
||||||
|
|
||||||
# Dynamically resolve prefetches for included serializer fields and attach them to the queryset
|
# Dynamically resolve prefetches for included serializer fields and attach them to the queryset
|
||||||
prefetch = get_prefetches_for_serializer(
|
if prefetch := get_prefetches_for_serializer(serializer_class, fields_to_include=self.requested_fields):
|
||||||
self.get_serializer_class(),
|
|
||||||
fields_to_include=self.requested_fields
|
|
||||||
)
|
|
||||||
if prefetch:
|
|
||||||
qs = qs.prefetch_related(*prefetch)
|
qs = qs.prefetch_related(*prefetch)
|
||||||
|
|
||||||
|
# Dynamically resolve annotations for RelatedObjectCountFields on the serializer and attach them to the queryset
|
||||||
|
if annotations := get_annotations_for_serializer(serializer_class, fields_to_include=self.requested_fields):
|
||||||
|
qs = qs.annotate(**annotations)
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
|
@ -52,19 +52,6 @@ class BriefModeMixin:
|
|||||||
|
|
||||||
return self.serializer_class
|
return self.serializer_class
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
qs = super().get_queryset()
|
|
||||||
|
|
||||||
if self.brief:
|
|
||||||
serializer_class = self.get_serializer_class()
|
|
||||||
|
|
||||||
# Clear any annotations for fields not present on the nested serializer
|
|
||||||
for annotation in list(qs.query.annotations.keys()):
|
|
||||||
if annotation not in serializer_class().fields:
|
|
||||||
qs.query.annotations.pop(annotation)
|
|
||||||
|
|
||||||
return qs
|
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldsMixin:
|
class CustomFieldsMixin:
|
||||||
"""
|
"""
|
||||||
|
@ -3,7 +3,7 @@ from drf_spectacular.types import OpenApiTypes
|
|||||||
from drf_spectacular.utils import extend_schema_field
|
from drf_spectacular.utils import extend_schema_field
|
||||||
from rest_framework import serializers
|
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.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
|
||||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||||
from tenancy.choices import ContactPriorityChoices
|
from tenancy.choices import ContactPriorityChoices
|
||||||
@ -32,16 +32,18 @@ class TenantGroupSerializer(NestedGroupModelSerializer):
|
|||||||
class TenantSerializer(NetBoxModelSerializer):
|
class TenantSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
|
||||||
group = NestedTenantGroupSerializer(required=False, allow_null=True)
|
group = NestedTenantGroupSerializer(required=False, allow_null=True)
|
||||||
circuit_count = serializers.IntegerField(read_only=True)
|
|
||||||
device_count = serializers.IntegerField(read_only=True)
|
# Related object counts
|
||||||
ipaddress_count = serializers.IntegerField(read_only=True)
|
circuit_count = RelatedObjectCountField('circuits')
|
||||||
prefix_count = serializers.IntegerField(read_only=True)
|
device_count = RelatedObjectCountField('devices')
|
||||||
rack_count = serializers.IntegerField(read_only=True)
|
rack_count = RelatedObjectCountField('racks')
|
||||||
site_count = serializers.IntegerField(read_only=True)
|
site_count = RelatedObjectCountField('sites')
|
||||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
ipaddress_count = RelatedObjectCountField('ip_addresses')
|
||||||
vlan_count = serializers.IntegerField(read_only=True)
|
prefix_count = RelatedObjectCountField('prefixes')
|
||||||
vrf_count = serializers.IntegerField(read_only=True)
|
vlan_count = RelatedObjectCountField('vlans')
|
||||||
cluster_count = serializers.IntegerField(read_only=True)
|
vrf_count = RelatedObjectCountField('vrfs')
|
||||||
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
cluster_count = RelatedObjectCountField('clusters')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tenant
|
model = Tenant
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
from rest_framework.routers import APIRootView
|
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 netbox.api.viewsets import NetBoxModelViewSet, MPTTLockedMixin
|
||||||
from tenancy import filtersets
|
from tenancy import filtersets
|
||||||
from tenancy.models import *
|
from tenancy.models import *
|
||||||
from utilities.utils import count_related
|
|
||||||
from virtualization.models import VirtualMachine, Cluster
|
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
@ -36,18 +31,7 @@ class TenantGroupViewSet(MPTTLockedMixin, NetBoxModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class TenantViewSet(NetBoxModelViewSet):
|
class TenantViewSet(NetBoxModelViewSet):
|
||||||
queryset = Tenant.objects.annotate(
|
queryset = Tenant.objects.all()
|
||||||
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')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.TenantSerializer
|
serializer_class = serializers.TenantSerializer
|
||||||
filterset_class = filtersets.TenantFilterSet
|
filterset_class = filtersets.TenantFilterSet
|
||||||
|
|
||||||
|
@ -11,10 +11,13 @@ from rest_framework import status
|
|||||||
from rest_framework.serializers import Serializer
|
from rest_framework.serializers import Serializer
|
||||||
from rest_framework.utils import formatting
|
from rest_framework.utils import formatting
|
||||||
|
|
||||||
|
from netbox.api.fields import RelatedObjectCountField
|
||||||
from netbox.api.exceptions import GraphQLTypeNotFound, SerializerNotFound
|
from netbox.api.exceptions import GraphQLTypeNotFound, SerializerNotFound
|
||||||
|
from utilities.utils import count_related
|
||||||
from .utils import dynamic_import
|
from .utils import dynamic_import
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'get_annotations_for_serializer',
|
||||||
'get_graphql_type_for_model',
|
'get_graphql_type_for_model',
|
||||||
'get_prefetches_for_serializer',
|
'get_prefetches_for_serializer',
|
||||||
'get_serializer_for_model',
|
'get_serializer_for_model',
|
||||||
@ -131,6 +134,26 @@ def get_prefetches_for_serializer(serializer_class, fields_to_include=None):
|
|||||||
return prefetch_fields
|
return prefetch_fields
|
||||||
|
|
||||||
|
|
||||||
|
def get_annotations_for_serializer(serializer_class, fields_to_include=None):
|
||||||
|
"""
|
||||||
|
Return a mapping of field names to annotations to be applied to the queryset for a serializer.
|
||||||
|
"""
|
||||||
|
annotations = {}
|
||||||
|
|
||||||
|
# If specific fields are not specified, default to all
|
||||||
|
if not fields_to_include:
|
||||||
|
fields_to_include = serializer_class.Meta.fields
|
||||||
|
|
||||||
|
model = serializer_class.Meta.model
|
||||||
|
|
||||||
|
for field_name, field in serializer_class._declared_fields.items():
|
||||||
|
if field_name in fields_to_include and type(field) is RelatedObjectCountField:
|
||||||
|
related_field = model._meta.get_field(field.relation).field
|
||||||
|
annotations[field_name] = count_related(related_field.model, related_field.name)
|
||||||
|
|
||||||
|
return annotations
|
||||||
|
|
||||||
|
|
||||||
def rest_api_server_error(request, *args, **kwargs):
|
def rest_api_server_error(request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Handle exceptions and return a useful error message for REST API requests.
|
Handle exceptions and return a useful error message for REST API requests.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from drf_spectacular.utils import extend_schema_serializer
|
from drf_spectacular.utils import extend_schema_serializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from netbox.api.fields import RelatedObjectCountField
|
||||||
from netbox.api.serializers import WritableNestedSerializer
|
from netbox.api.serializers import WritableNestedSerializer
|
||||||
from virtualization.models import *
|
from virtualization.models import *
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ __all__ = [
|
|||||||
)
|
)
|
||||||
class NestedClusterTypeSerializer(WritableNestedSerializer):
|
class NestedClusterTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
|
||||||
cluster_count = serializers.IntegerField(read_only=True)
|
cluster_count = RelatedObjectCountField('clusters')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ClusterType
|
model = ClusterType
|
||||||
@ -35,7 +36,7 @@ class NestedClusterTypeSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedClusterGroupSerializer(WritableNestedSerializer):
|
class NestedClusterGroupSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
|
||||||
cluster_count = serializers.IntegerField(read_only=True)
|
cluster_count = RelatedObjectCountField('clusters')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ClusterGroup
|
model = ClusterGroup
|
||||||
@ -47,7 +48,7 @@ class NestedClusterGroupSerializer(WritableNestedSerializer):
|
|||||||
)
|
)
|
||||||
class NestedClusterSerializer(WritableNestedSerializer):
|
class NestedClusterSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
|
||||||
virtualmachine_count = serializers.IntegerField(read_only=True)
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cluster
|
model = Cluster
|
||||||
|
@ -8,7 +8,7 @@ from dcim.choices import InterfaceModeChoices
|
|||||||
from extras.api.nested_serializers import NestedConfigTemplateSerializer
|
from extras.api.nested_serializers import NestedConfigTemplateSerializer
|
||||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer
|
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer
|
||||||
from ipam.models import VLAN
|
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 netbox.api.serializers import NetBoxModelSerializer
|
||||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||||
from virtualization.choices import *
|
from virtualization.choices import *
|
||||||
@ -23,7 +23,9 @@ from .nested_serializers import *
|
|||||||
|
|
||||||
class ClusterTypeSerializer(NetBoxModelSerializer):
|
class ClusterTypeSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
|
||||||
cluster_count = serializers.IntegerField(read_only=True)
|
|
||||||
|
# Related object counts
|
||||||
|
cluster_count = RelatedObjectCountField('clusters')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ClusterType
|
model = ClusterType
|
||||||
@ -35,7 +37,9 @@ class ClusterTypeSerializer(NetBoxModelSerializer):
|
|||||||
|
|
||||||
class ClusterGroupSerializer(NetBoxModelSerializer):
|
class ClusterGroupSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
|
||||||
cluster_count = serializers.IntegerField(read_only=True)
|
|
||||||
|
# Related object counts
|
||||||
|
cluster_count = RelatedObjectCountField('clusters')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ClusterGroup
|
model = ClusterGroup
|
||||||
@ -52,8 +56,10 @@ class ClusterSerializer(NetBoxModelSerializer):
|
|||||||
status = ChoiceField(choices=ClusterStatusChoices, required=False)
|
status = ChoiceField(choices=ClusterStatusChoices, required=False)
|
||||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
site = NestedSiteSerializer(required=False, allow_null=True, default=None)
|
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('devices')
|
||||||
|
virtualmachine_count = RelatedObjectCountField('virtual_machines')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cluster
|
model = Cluster
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
|
|
||||||
from dcim.models import Device
|
|
||||||
from extras.api.mixins import ConfigContextQuerySetMixin, RenderConfigMixin
|
from extras.api.mixins import ConfigContextQuerySetMixin, RenderConfigMixin
|
||||||
from netbox.api.viewsets import NetBoxModelViewSet
|
from netbox.api.viewsets import NetBoxModelViewSet
|
||||||
from utilities.query_functions import CollateAsChar
|
from utilities.query_functions import CollateAsChar
|
||||||
from utilities.utils import count_related
|
|
||||||
from virtualization import filtersets
|
from virtualization import filtersets
|
||||||
from virtualization.models import *
|
from virtualization.models import *
|
||||||
from . import serializers
|
from . import serializers
|
||||||
@ -23,26 +21,19 @@ class VirtualizationRootView(APIRootView):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class ClusterTypeViewSet(NetBoxModelViewSet):
|
class ClusterTypeViewSet(NetBoxModelViewSet):
|
||||||
queryset = ClusterType.objects.annotate(
|
queryset = ClusterType.objects.all()
|
||||||
cluster_count=count_related(Cluster, 'type')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.ClusterTypeSerializer
|
serializer_class = serializers.ClusterTypeSerializer
|
||||||
filterset_class = filtersets.ClusterTypeFilterSet
|
filterset_class = filtersets.ClusterTypeFilterSet
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupViewSet(NetBoxModelViewSet):
|
class ClusterGroupViewSet(NetBoxModelViewSet):
|
||||||
queryset = ClusterGroup.objects.annotate(
|
queryset = ClusterGroup.objects.all()
|
||||||
cluster_count=count_related(Cluster, 'group')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.ClusterGroupSerializer
|
serializer_class = serializers.ClusterGroupSerializer
|
||||||
filterset_class = filtersets.ClusterGroupFilterSet
|
filterset_class = filtersets.ClusterGroupFilterSet
|
||||||
|
|
||||||
|
|
||||||
class ClusterViewSet(NetBoxModelViewSet):
|
class ClusterViewSet(NetBoxModelViewSet):
|
||||||
queryset = Cluster.objects.annotate(
|
queryset = Cluster.objects.all()
|
||||||
device_count=count_related(Device, 'cluster'),
|
|
||||||
virtualmachine_count=count_related(VirtualMachine, 'cluster')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.ClusterSerializer
|
serializer_class = serializers.ClusterSerializer
|
||||||
filterset_class = filtersets.ClusterFilterSet
|
filterset_class = filtersets.ClusterFilterSet
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from drf_spectacular.utils import extend_schema_serializer
|
from drf_spectacular.utils import extend_schema_serializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from netbox.api.fields import RelatedObjectCountField
|
||||||
from netbox.api.serializers import WritableNestedSerializer
|
from netbox.api.serializers import WritableNestedSerializer
|
||||||
from vpn import models
|
from vpn import models
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
class NestedTunnelGroupSerializer(WritableNestedSerializer):
|
class NestedTunnelGroupSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
|
||||||
tunnel_count = serializers.IntegerField(read_only=True)
|
tunnel_count = RelatedObjectCountField('tunnels')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.TunnelGroup
|
model = models.TunnelGroup
|
||||||
|
@ -4,7 +4,7 @@ from rest_framework import serializers
|
|||||||
|
|
||||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedRouteTargetSerializer
|
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedRouteTargetSerializer
|
||||||
from ipam.models import RouteTarget
|
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.api.serializers import NetBoxModelSerializer
|
||||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||||
@ -29,7 +29,9 @@ __all__ = (
|
|||||||
|
|
||||||
class TunnelGroupSerializer(NetBoxModelSerializer):
|
class TunnelGroupSerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
|
||||||
tunnel_count = serializers.IntegerField(read_only=True)
|
|
||||||
|
# Related object counts
|
||||||
|
tunnel_count = RelatedObjectCountField('tunnels')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TunnelGroup
|
model = TunnelGroup
|
||||||
@ -59,11 +61,14 @@ class TunnelSerializer(NetBoxModelSerializer):
|
|||||||
allow_null=True
|
allow_null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Related object counts
|
||||||
|
terminations_count = RelatedObjectCountField('terminations')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tunnel
|
model = Tunnel
|
||||||
fields = (
|
fields = (
|
||||||
'id', 'url', 'display', 'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id',
|
'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',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
|
|
||||||
from netbox.api.viewsets import NetBoxModelViewSet
|
from netbox.api.viewsets import NetBoxModelViewSet
|
||||||
from utilities.utils import count_related
|
|
||||||
from vpn import filtersets
|
from vpn import filtersets
|
||||||
from vpn.models import *
|
from vpn.models import *
|
||||||
from . import serializers
|
from . import serializers
|
||||||
@ -34,17 +33,13 @@ class VPNRootView(APIRootView):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class TunnelGroupViewSet(NetBoxModelViewSet):
|
class TunnelGroupViewSet(NetBoxModelViewSet):
|
||||||
queryset = TunnelGroup.objects.annotate(
|
queryset = TunnelGroup.objects.all()
|
||||||
tunnel_count=count_related(Tunnel, 'group')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.TunnelGroupSerializer
|
serializer_class = serializers.TunnelGroupSerializer
|
||||||
filterset_class = filtersets.TunnelGroupFilterSet
|
filterset_class = filtersets.TunnelGroupFilterSet
|
||||||
|
|
||||||
|
|
||||||
class TunnelViewSet(NetBoxModelViewSet):
|
class TunnelViewSet(NetBoxModelViewSet):
|
||||||
queryset = Tunnel.objects.annotate(
|
queryset = Tunnel.objects.all()
|
||||||
terminations_count=count_related(TunnelTermination, 'tunnel')
|
|
||||||
)
|
|
||||||
serializer_class = serializers.TunnelSerializer
|
serializer_class = serializers.TunnelSerializer
|
||||||
filterset_class = filtersets.TunnelFilterSet
|
filterset_class = filtersets.TunnelFilterSet
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user