jeremystretch 3 лет назад
Родитель
Сommit
a11abf87ec

+ 5 - 5
netbox/circuits/api/serializers.py

@@ -5,7 +5,7 @@ from circuits.models import *
 from dcim.api.nested_serializers import NestedCableSerializer, NestedSiteSerializer
 from dcim.api.nested_serializers import NestedCableSerializer, NestedSiteSerializer
 from dcim.api.serializers import LinkTerminationSerializer
 from dcim.api.serializers import LinkTerminationSerializer
 from netbox.api import ChoiceField
 from netbox.api import ChoiceField
-from netbox.api.serializers import PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer
+from netbox.api.serializers import NetBoxModelSerializer, ValidatedModelSerializer, WritableNestedSerializer
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from .nested_serializers import *
 from .nested_serializers import *
 
 
@@ -14,7 +14,7 @@ from .nested_serializers import *
 # Providers
 # Providers
 #
 #
 
 
-class ProviderSerializer(PrimaryModelSerializer):
+class ProviderSerializer(NetBoxModelSerializer):
     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 = serializers.IntegerField(read_only=True)
 
 
@@ -30,7 +30,7 @@ class ProviderSerializer(PrimaryModelSerializer):
 # Provider networks
 # Provider networks
 #
 #
 
 
-class ProviderNetworkSerializer(PrimaryModelSerializer):
+class ProviderNetworkSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
     provider = NestedProviderSerializer()
     provider = NestedProviderSerializer()
 
 
@@ -46,7 +46,7 @@ class ProviderNetworkSerializer(PrimaryModelSerializer):
 # Circuits
 # Circuits
 #
 #
 
 
-class CircuitTypeSerializer(PrimaryModelSerializer):
+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)
     circuit_count = serializers.IntegerField(read_only=True)
 
 
@@ -70,7 +70,7 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
         ]
         ]
 
 
 
 
-class CircuitSerializer(PrimaryModelSerializer):
+class CircuitSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
     provider = NestedProviderSerializer()
     provider = NestedProviderSerializer()
     status = ChoiceField(choices=CircuitStatusChoices, required=False)
     status = ChoiceField(choices=CircuitStatusChoices, required=False)

+ 27 - 27
netbox/dcim/api/serializers.py

@@ -12,7 +12,7 @@ from ipam.api.nested_serializers import (
 from ipam.models import ASN, VLAN
 from ipam.models import ASN, VLAN
 from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
 from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
 from netbox.api.serializers import (
 from netbox.api.serializers import (
-    NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
+    NestedGroupModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
 )
 )
 from netbox.config import ConfigItem
 from netbox.config import ConfigItem
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from tenancy.api.nested_serializers import NestedTenantSerializer
@@ -109,7 +109,7 @@ class SiteGroupSerializer(NestedGroupModelSerializer):
         ]
         ]
 
 
 
 
-class SiteSerializer(PrimaryModelSerializer):
+class SiteSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
     status = ChoiceField(choices=SiteStatusChoices, required=False)
     status = ChoiceField(choices=SiteStatusChoices, required=False)
     region = NestedRegionSerializer(required=False, allow_null=True)
     region = NestedRegionSerializer(required=False, allow_null=True)
@@ -161,7 +161,7 @@ class LocationSerializer(NestedGroupModelSerializer):
         ]
         ]
 
 
 
 
-class RackRoleSerializer(PrimaryModelSerializer):
+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)
     rack_count = serializers.IntegerField(read_only=True)
 
 
@@ -173,7 +173,7 @@ class RackRoleSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class RackSerializer(PrimaryModelSerializer):
+class RackSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
     site = NestedSiteSerializer()
     site = NestedSiteSerializer()
     location = NestedLocationSerializer(required=False, allow_null=True, default=None)
     location = NestedLocationSerializer(required=False, allow_null=True, default=None)
@@ -212,7 +212,7 @@ class RackUnitSerializer(serializers.Serializer):
         return obj['name']
         return obj['name']
 
 
 
 
-class RackReservationSerializer(PrimaryModelSerializer):
+class RackReservationSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
     rack = NestedRackSerializer()
     rack = NestedRackSerializer()
     user = NestedUserSerializer()
     user = NestedUserSerializer()
@@ -266,7 +266,7 @@ class RackElevationDetailFilterSerializer(serializers.Serializer):
 # Device/module types
 # Device/module types
 #
 #
 
 
-class ManufacturerSerializer(PrimaryModelSerializer):
+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)
     devicetype_count = serializers.IntegerField(read_only=True)
     inventoryitem_count = serializers.IntegerField(read_only=True)
     inventoryitem_count = serializers.IntegerField(read_only=True)
@@ -280,7 +280,7 @@ class ManufacturerSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class DeviceTypeSerializer(PrimaryModelSerializer):
+class DeviceTypeSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
     subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
     subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
@@ -296,7 +296,7 @@ class DeviceTypeSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class ModuleTypeSerializer(PrimaryModelSerializer):
+class ModuleTypeSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
     # module_count = serializers.IntegerField(read_only=True)
     # module_count = serializers.IntegerField(read_only=True)
@@ -487,7 +487,7 @@ class InventoryItemTemplateSerializer(ValidatedModelSerializer):
 # Devices
 # Devices
 #
 #
 
 
-class DeviceRoleSerializer(PrimaryModelSerializer):
+class DeviceRoleSerializer(NetBoxModelSerializer):
     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 = serializers.IntegerField(read_only=True)
     virtualmachine_count = serializers.IntegerField(read_only=True)
     virtualmachine_count = serializers.IntegerField(read_only=True)
@@ -500,7 +500,7 @@ class DeviceRoleSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class PlatformSerializer(PrimaryModelSerializer):
+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)
     device_count = serializers.IntegerField(read_only=True)
     device_count = serializers.IntegerField(read_only=True)
@@ -514,7 +514,7 @@ class PlatformSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class DeviceSerializer(PrimaryModelSerializer):
+class DeviceSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
     device_type = NestedDeviceTypeSerializer()
     device_type = NestedDeviceTypeSerializer()
     device_role = NestedDeviceRoleSerializer()
     device_role = NestedDeviceRoleSerializer()
@@ -556,7 +556,7 @@ class DeviceSerializer(PrimaryModelSerializer):
         return data
         return data
 
 
 
 
-class ModuleSerializer(PrimaryModelSerializer):
+class ModuleSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module_bay = NestedModuleBaySerializer()
     module_bay = NestedModuleBaySerializer()
@@ -594,7 +594,7 @@ class DeviceNAPALMSerializer(serializers.Serializer):
 # Device components
 # Device components
 #
 #
 
 
-class ConsoleServerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
+class ConsoleServerPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module = ComponentNestedModuleSerializer(
     module = ComponentNestedModuleSerializer(
@@ -622,7 +622,7 @@ class ConsoleServerPortSerializer(PrimaryModelSerializer, LinkTerminationSeriali
         ]
         ]
 
 
 
 
-class ConsolePortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
+class ConsolePortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module = ComponentNestedModuleSerializer(
     module = ComponentNestedModuleSerializer(
@@ -650,7 +650,7 @@ class ConsolePortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, C
         ]
         ]
 
 
 
 
-class PowerOutletSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
+class PowerOutletSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module = ComponentNestedModuleSerializer(
     module = ComponentNestedModuleSerializer(
@@ -685,7 +685,7 @@ class PowerOutletSerializer(PrimaryModelSerializer, LinkTerminationSerializer, C
         ]
         ]
 
 
 
 
-class PowerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
+class PowerPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module = ComponentNestedModuleSerializer(
     module = ComponentNestedModuleSerializer(
@@ -709,7 +709,7 @@ class PowerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
         ]
         ]
 
 
 
 
-class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
+class InterfaceSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module = ComponentNestedModuleSerializer(
     module = ComponentNestedModuleSerializer(
@@ -768,7 +768,7 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
         return super().validate(data)
         return super().validate(data)
 
 
 
 
-class RearPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer):
+class RearPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module = ComponentNestedModuleSerializer(
     module = ComponentNestedModuleSerializer(
@@ -798,7 +798,7 @@ class FrontPortRearPortSerializer(WritableNestedSerializer):
         fields = ['id', 'url', 'display', 'name', 'label']
         fields = ['id', 'url', 'display', 'name', 'label']
 
 
 
 
-class FrontPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer):
+class FrontPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     module = ComponentNestedModuleSerializer(
     module = ComponentNestedModuleSerializer(
@@ -818,7 +818,7 @@ class FrontPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer):
         ]
         ]
 
 
 
 
-class ModuleBaySerializer(PrimaryModelSerializer):
+class ModuleBaySerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     # installed_module = NestedModuleSerializer(required=False, allow_null=True)
     # installed_module = NestedModuleSerializer(required=False, allow_null=True)
@@ -831,7 +831,7 @@ class ModuleBaySerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class DeviceBaySerializer(PrimaryModelSerializer):
+class DeviceBaySerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     installed_device = NestedDeviceSerializer(required=False, allow_null=True)
     installed_device = NestedDeviceSerializer(required=False, allow_null=True)
@@ -844,7 +844,7 @@ class DeviceBaySerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class InventoryItemSerializer(PrimaryModelSerializer):
+class InventoryItemSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
     parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
     parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
@@ -879,7 +879,7 @@ class InventoryItemSerializer(PrimaryModelSerializer):
 # Device component roles
 # Device component roles
 #
 #
 
 
-class InventoryItemRoleSerializer(PrimaryModelSerializer):
+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)
     inventoryitem_count = serializers.IntegerField(read_only=True)
 
 
@@ -895,7 +895,7 @@ class InventoryItemRoleSerializer(PrimaryModelSerializer):
 # Cables
 # Cables
 #
 #
 
 
-class CableSerializer(PrimaryModelSerializer):
+class CableSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
     termination_a_type = ContentTypeField(
     termination_a_type = ContentTypeField(
         queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
         queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
@@ -1001,7 +1001,7 @@ class CablePathSerializer(serializers.ModelSerializer):
 # Virtual chassis
 # Virtual chassis
 #
 #
 
 
-class VirtualChassisSerializer(PrimaryModelSerializer):
+class VirtualChassisSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
     master = NestedDeviceSerializer(required=False)
     master = NestedDeviceSerializer(required=False)
     member_count = serializers.IntegerField(read_only=True)
     member_count = serializers.IntegerField(read_only=True)
@@ -1018,7 +1018,7 @@ class VirtualChassisSerializer(PrimaryModelSerializer):
 # Power panels
 # Power panels
 #
 #
 
 
-class PowerPanelSerializer(PrimaryModelSerializer):
+class PowerPanelSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
     site = NestedSiteSerializer()
     site = NestedSiteSerializer()
     location = NestedLocationSerializer(
     location = NestedLocationSerializer(
@@ -1036,7 +1036,7 @@ class PowerPanelSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class PowerFeedSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
+class PowerFeedSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
     power_panel = NestedPowerPanelSerializer()
     power_panel = NestedPowerPanelSerializer()
     rack = NestedRackSerializer(
     rack = NestedRackSerializer(

+ 17 - 17
netbox/ipam/api/serializers.py

@@ -9,7 +9,7 @@ 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 import ChoiceField, ContentTypeField, SerializedPKRelatedField
 from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
-from netbox.api.serializers import PrimaryModelSerializer
+from netbox.api.serializers import NetBoxModelSerializer
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from tenancy.api.nested_serializers import NestedTenantSerializer
 from utilities.api import get_serializer_for_model
 from utilities.api import get_serializer_for_model
 from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
 from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
@@ -20,7 +20,7 @@ from .nested_serializers import *
 # ASNs
 # ASNs
 #
 #
 
 
-class ASNSerializer(PrimaryModelSerializer):
+class ASNSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     site_count = serializers.IntegerField(read_only=True)
     site_count = serializers.IntegerField(read_only=True)
@@ -37,7 +37,7 @@ class ASNSerializer(PrimaryModelSerializer):
 # VRFs
 # VRFs
 #
 #
 
 
-class VRFSerializer(PrimaryModelSerializer):
+class VRFSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     import_targets = SerializedPKRelatedField(
     import_targets = SerializedPKRelatedField(
@@ -67,7 +67,7 @@ class VRFSerializer(PrimaryModelSerializer):
 # Route targets
 # Route targets
 #
 #
 
 
-class RouteTargetSerializer(PrimaryModelSerializer):
+class RouteTargetSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
 
 
@@ -82,7 +82,7 @@ class RouteTargetSerializer(PrimaryModelSerializer):
 # RIRs/aggregates
 # RIRs/aggregates
 #
 #
 
 
-class RIRSerializer(PrimaryModelSerializer):
+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)
     aggregate_count = serializers.IntegerField(read_only=True)
 
 
@@ -94,7 +94,7 @@ class RIRSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class AggregateSerializer(PrimaryModelSerializer):
+class AggregateSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     rir = NestedRIRSerializer()
     rir = NestedRIRSerializer()
@@ -113,7 +113,7 @@ class AggregateSerializer(PrimaryModelSerializer):
 # FHRP Groups
 # FHRP Groups
 #
 #
 
 
-class FHRPGroupSerializer(PrimaryModelSerializer):
+class FHRPGroupSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroup-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroup-detail')
     ip_addresses = NestedIPAddressSerializer(many=True, read_only=True)
     ip_addresses = NestedIPAddressSerializer(many=True, read_only=True)
 
 
@@ -125,7 +125,7 @@ class FHRPGroupSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class FHRPGroupAssignmentSerializer(PrimaryModelSerializer):
+class FHRPGroupAssignmentSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroupassignment-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroupassignment-detail')
     group = NestedFHRPGroupSerializer()
     group = NestedFHRPGroupSerializer()
     interface_type = ContentTypeField(
     interface_type = ContentTypeField(
@@ -153,7 +153,7 @@ class FHRPGroupAssignmentSerializer(PrimaryModelSerializer):
 # VLANs
 # VLANs
 #
 #
 
 
-class RoleSerializer(PrimaryModelSerializer):
+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)
     prefix_count = serializers.IntegerField(read_only=True)
     vlan_count = serializers.IntegerField(read_only=True)
     vlan_count = serializers.IntegerField(read_only=True)
@@ -166,7 +166,7 @@ class RoleSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class VLANGroupSerializer(PrimaryModelSerializer):
+class VLANGroupSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
     scope_type = ContentTypeField(
     scope_type = ContentTypeField(
         queryset=ContentType.objects.filter(
         queryset=ContentType.objects.filter(
@@ -196,7 +196,7 @@ class VLANGroupSerializer(PrimaryModelSerializer):
         return serializer(obj.scope, context=context).data
         return serializer(obj.scope, context=context).data
 
 
 
 
-class VLANSerializer(PrimaryModelSerializer):
+class VLANSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
     site = NestedSiteSerializer(required=False, allow_null=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
     group = NestedVLANGroupSerializer(required=False, allow_null=True, default=None)
     group = NestedVLANGroupSerializer(required=False, allow_null=True, default=None)
@@ -230,7 +230,7 @@ class AvailableVLANSerializer(serializers.Serializer):
         ])
         ])
 
 
 
 
-class CreateAvailableVLANSerializer(PrimaryModelSerializer):
+class CreateAvailableVLANSerializer(NetBoxModelSerializer):
     site = NestedSiteSerializer(required=False, allow_null=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     tenant = NestedTenantSerializer(required=False, allow_null=True)
     status = ChoiceField(choices=VLANStatusChoices, required=False)
     status = ChoiceField(choices=VLANStatusChoices, required=False)
@@ -251,7 +251,7 @@ class CreateAvailableVLANSerializer(PrimaryModelSerializer):
 # Prefixes
 # Prefixes
 #
 #
 
 
-class PrefixSerializer(PrimaryModelSerializer):
+class PrefixSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
     site = NestedSiteSerializer(required=False, allow_null=True)
@@ -323,7 +323,7 @@ class AvailablePrefixSerializer(serializers.Serializer):
 # IP ranges
 # IP ranges
 #
 #
 
 
-class IPRangeSerializer(PrimaryModelSerializer):
+class IPRangeSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail')
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
@@ -345,7 +345,7 @@ class IPRangeSerializer(PrimaryModelSerializer):
 # IP addresses
 # IP addresses
 #
 #
 
 
-class IPAddressSerializer(PrimaryModelSerializer):
+class IPAddressSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
     vrf = NestedVRFSerializer(required=False, allow_null=True)
@@ -403,7 +403,7 @@ class AvailableIPSerializer(serializers.Serializer):
 # Services
 # Services
 #
 #
 
 
-class ServiceTemplateSerializer(PrimaryModelSerializer):
+class ServiceTemplateSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:servicetemplate-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:servicetemplate-detail')
     protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
     protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
 
 
@@ -415,7 +415,7 @@ class ServiceTemplateSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class ServiceSerializer(PrimaryModelSerializer):
+class ServiceSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
     device = NestedDeviceSerializer(required=False, allow_null=True)
     device = NestedDeviceSerializer(required=False, allow_null=True)
     virtual_machine = NestedVirtualMachineSerializer(required=False, allow_null=True)
     virtual_machine = NestedVirtualMachineSerializer(required=False, allow_null=True)

+ 0 - 193
netbox/netbox/api/serializers.py

@@ -1,193 +0,0 @@
-from django.contrib.contenttypes.models import ContentType
-from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist
-from django.db.models import ManyToManyField
-from rest_framework import serializers
-from rest_framework.exceptions import ValidationError
-from rest_framework.fields import CreateOnlyDefault
-
-from extras.api.customfields import CustomFieldsDataField, CustomFieldDefaultValues
-from extras.models import CustomField, Tag
-from utilities.utils import dict_to_filter_params
-
-
-class BaseModelSerializer(serializers.ModelSerializer):
-    display = serializers.SerializerMethodField(read_only=True)
-
-    def get_display(self, obj):
-        return str(obj)
-
-
-class ValidatedModelSerializer(BaseModelSerializer):
-    """
-    Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during
-    validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
-    """
-    def validate(self, data):
-
-        # Remove custom fields data and tags (if any) prior to model validation
-        attrs = data.copy()
-        attrs.pop('custom_fields', None)
-        attrs.pop('tags', None)
-
-        # Skip ManyToManyFields
-        for field in self.Meta.model._meta.get_fields():
-            if isinstance(field, ManyToManyField):
-                attrs.pop(field.name, None)
-
-        # Run clean() on an instance of the model
-        if self.instance is None:
-            instance = self.Meta.model(**attrs)
-        else:
-            instance = self.instance
-            for k, v in attrs.items():
-                setattr(instance, k, v)
-        instance.full_clean()
-
-        return data
-
-
-class CustomFieldModelSerializer(ValidatedModelSerializer):
-    """
-    Extends ModelSerializer to render any CustomFields and their values associated with an object.
-    """
-    custom_fields = CustomFieldsDataField(
-        source='custom_field_data',
-        default=CreateOnlyDefault(CustomFieldDefaultValues())
-    )
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-
-        if self.instance is not None:
-
-            # Retrieve the set of CustomFields which apply to this type of object
-            content_type = ContentType.objects.get_for_model(self.Meta.model)
-            fields = CustomField.objects.filter(content_types=content_type)
-
-            # Populate CustomFieldValues for each instance from database
-            if type(self.instance) in (list, tuple):
-                for obj in self.instance:
-                    self._populate_custom_fields(obj, fields)
-            else:
-                self._populate_custom_fields(self.instance, fields)
-
-    def _populate_custom_fields(self, instance, custom_fields):
-        instance.custom_fields = {}
-        for field in custom_fields:
-            instance.custom_fields[field.name] = instance.cf.get(field.name)
-
-
-#
-# Nested serializers
-#
-
-class WritableNestedSerializer(BaseModelSerializer):
-    """
-    Returns a nested representation of an object on read, but accepts only a primary key on write.
-    """
-    def to_internal_value(self, data):
-
-        if data is None:
-            return None
-
-        # Dictionary of related object attributes
-        if isinstance(data, dict):
-            params = dict_to_filter_params(data)
-            queryset = self.Meta.model.objects
-            try:
-                return queryset.get(**params)
-            except ObjectDoesNotExist:
-                raise ValidationError(
-                    "Related object not found using the provided attributes: {}".format(params)
-                )
-            except MultipleObjectsReturned:
-                raise ValidationError(
-                    "Multiple objects match the provided attributes: {}".format(params)
-                )
-            except FieldError as e:
-                raise ValidationError(e)
-
-        # Integer PK of related object
-        if isinstance(data, int):
-            pk = data
-        else:
-            try:
-                # PK might have been mistakenly passed as a string
-                pk = int(data)
-            except (TypeError, ValueError):
-                raise ValidationError(
-                    "Related objects must be referenced by numeric ID or by dictionary of attributes. Received an "
-                    "unrecognized value: {}".format(data)
-                )
-
-        # Look up object by PK
-        queryset = self.Meta.model.objects
-        try:
-            return queryset.get(pk=int(data))
-        except ObjectDoesNotExist:
-            raise ValidationError(
-                "Related object not found using the provided numeric ID: {}".format(pk)
-            )
-
-
-#
-# Nested tags serialization
-#
-
-# Declared here for use by PrimaryModelSerializer, but should be imported from extras.api.nested_serializers
-class NestedTagSerializer(WritableNestedSerializer):
-    url = serializers.HyperlinkedIdentityField(view_name='extras-api:tag-detail')
-
-    class Meta:
-        model = Tag
-        fields = ['id', 'url', 'display', 'name', 'slug', 'color']
-
-
-#
-# Base model serializers
-#
-
-class PrimaryModelSerializer(CustomFieldModelSerializer):
-    """
-    Adds support for custom fields and tags.
-    """
-    tags = NestedTagSerializer(many=True, required=False)
-
-    def create(self, validated_data):
-        tags = validated_data.pop('tags', None)
-        instance = super().create(validated_data)
-
-        if tags is not None:
-            return self._save_tags(instance, tags)
-        return instance
-
-    def update(self, instance, validated_data):
-        tags = validated_data.pop('tags', None)
-
-        # Cache tags on instance for change logging
-        instance._tags = tags or []
-
-        instance = super().update(instance, validated_data)
-
-        if tags is not None:
-            return self._save_tags(instance, tags)
-        return instance
-
-    def _save_tags(self, instance, tags):
-        if tags:
-            instance.tags.set([t.name for t in tags])
-        else:
-            instance.tags.clear()
-
-        return instance
-
-
-class NestedGroupModelSerializer(PrimaryModelSerializer):
-    """
-    Extends PrimaryModelSerializer to include MPTT support.
-    """
-    _depth = serializers.IntegerField(source='level', read_only=True)
-
-
-class BulkOperationSerializer(serializers.Serializer):
-    id = serializers.IntegerField()

+ 27 - 0
netbox/netbox/api/serializers/__init__.py

@@ -0,0 +1,27 @@
+from rest_framework import serializers
+
+from .base import *
+from .features import *
+from .nested import *
+
+
+#
+# Base model serializers
+#
+
+class NetBoxModelSerializer(TaggableObjectSerializer, CustomFieldModelSerializer, ValidatedModelSerializer):
+    """
+    Adds support for custom fields and tags.
+    """
+    pass
+
+
+class NestedGroupModelSerializer(NetBoxModelSerializer):
+    """
+    Extends PrimaryModelSerializer to include MPTT support.
+    """
+    _depth = serializers.IntegerField(source='level', read_only=True)
+
+
+class BulkOperationSerializer(serializers.Serializer):
+    id = serializers.IntegerField()

+ 43 - 0
netbox/netbox/api/serializers/base.py

@@ -0,0 +1,43 @@
+from django.db.models import ManyToManyField
+from rest_framework import serializers
+
+__all__ = (
+    'BaseModelSerializer',
+    'ValidatedModelSerializer',
+)
+
+
+class BaseModelSerializer(serializers.ModelSerializer):
+    display = serializers.SerializerMethodField(read_only=True)
+
+    def get_display(self, obj):
+        return str(obj)
+
+
+class ValidatedModelSerializer(BaseModelSerializer):
+    """
+    Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during
+    validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
+    """
+    def validate(self, data):
+
+        # Remove custom fields data and tags (if any) prior to model validation
+        attrs = data.copy()
+        attrs.pop('custom_fields', None)
+        attrs.pop('tags', None)
+
+        # Skip ManyToManyFields
+        for field in self.Meta.model._meta.get_fields():
+            if isinstance(field, ManyToManyField):
+                attrs.pop(field.name, None)
+
+        # Run clean() on an instance of the model
+        if self.instance is None:
+            instance = self.Meta.model(**attrs)
+        else:
+            instance = self.instance
+            for k, v in attrs.items():
+                setattr(instance, k, v)
+        instance.full_clean()
+
+        return data

+ 80 - 0
netbox/netbox/api/serializers/features.py

@@ -0,0 +1,80 @@
+from django.contrib.contenttypes.models import ContentType
+from rest_framework import serializers
+from rest_framework.fields import CreateOnlyDefault
+
+from extras.api.customfields import CustomFieldsDataField, CustomFieldDefaultValues
+from extras.models import CustomField
+from .nested import NestedTagSerializer
+
+__all__ = (
+    'CustomFieldModelSerializer',
+    'TaggableObjectSerializer',
+)
+
+
+class CustomFieldModelSerializer(serializers.Serializer):
+    """
+    Introduces support for custom field assignment. Adds `custom_fields` serialization and ensures
+    that custom field data is populated upon initialization.
+    """
+    custom_fields = CustomFieldsDataField(
+        source='custom_field_data',
+        default=CreateOnlyDefault(CustomFieldDefaultValues())
+    )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        if self.instance is not None:
+
+            # Retrieve the set of CustomFields which apply to this type of object
+            content_type = ContentType.objects.get_for_model(self.Meta.model)
+            fields = CustomField.objects.filter(content_types=content_type)
+
+            # Populate custom field values for each instance from database
+            if type(self.instance) in (list, tuple):
+                for obj in self.instance:
+                    self._populate_custom_fields(obj, fields)
+            else:
+                self._populate_custom_fields(self.instance, fields)
+
+    def _populate_custom_fields(self, instance, custom_fields):
+        instance.custom_fields = {}
+        for field in custom_fields:
+            instance.custom_fields[field.name] = instance.cf.get(field.name)
+
+
+class TaggableObjectSerializer(serializers.Serializer):
+    """
+    Introduces support for Tag assignment. Adds `tags` serialization, and handles tag assignment
+    on create() and update().
+    """
+    tags = NestedTagSerializer(many=True, required=False)
+
+    def create(self, validated_data):
+        tags = validated_data.pop('tags', None)
+        instance = super().create(validated_data)
+
+        if tags is not None:
+            return self._save_tags(instance, tags)
+        return instance
+
+    def update(self, instance, validated_data):
+        tags = validated_data.pop('tags', None)
+
+        # Cache tags on instance for change logging
+        instance._tags = tags or []
+
+        instance = super().update(instance, validated_data)
+
+        if tags is not None:
+            return self._save_tags(instance, tags)
+        return instance
+
+    def _save_tags(self, instance, tags):
+        if tags:
+            instance.tags.set([t.name for t in tags])
+        else:
+            instance.tags.clear()
+
+        return instance

+ 62 - 0
netbox/netbox/api/serializers/nested.py

@@ -0,0 +1,62 @@
+from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist
+from rest_framework import serializers
+from rest_framework.exceptions import ValidationError
+
+from extras.models import Tag
+from utilities.utils import dict_to_filter_params
+from .base import BaseModelSerializer
+
+__all__ = (
+    'NestedTagSerializer',
+    'WritableNestedSerializer',
+)
+
+
+class WritableNestedSerializer(BaseModelSerializer):
+    """
+    Represents an object related through a ForeignKey field. On write, it accepts a primary key (PK) value or a
+    dictionary of attributes which can be used to uniquely identify the related object. This class should be
+    subclassed to return a full representation of the related object on read.
+    """
+    def to_internal_value(self, data):
+
+        if data is None:
+            return None
+
+        # Dictionary of related object attributes
+        if isinstance(data, dict):
+            params = dict_to_filter_params(data)
+            queryset = self.Meta.model.objects
+            try:
+                return queryset.get(**params)
+            except ObjectDoesNotExist:
+                raise ValidationError(f"Related object not found using the provided attributes: {params}")
+            except MultipleObjectsReturned:
+                raise ValidationError(f"Multiple objects match the provided attributes: {params}")
+            except FieldError as e:
+                raise ValidationError(e)
+
+        # Integer PK of related object
+        try:
+            # Cast as integer in case a PK was mistakenly sent as a string
+            pk = int(data)
+        except (TypeError, ValueError):
+            raise ValidationError(
+                f"Related objects must be referenced by numeric ID or by dictionary of attributes. Received an "
+                f"unrecognized value: {data}"
+            )
+
+        # Look up object by PK
+        try:
+            return self.Meta.model.objects.get(pk=pk)
+        except ObjectDoesNotExist:
+            raise ValidationError(f"Related object not found using the provided numeric ID: {pk}")
+
+
+# Declared here for use by PrimaryModelSerializer, but should be imported from extras.api.nested_serializers
+class NestedTagSerializer(WritableNestedSerializer):
+    url = serializers.HyperlinkedIdentityField(view_name='extras-api:tag-detail')
+
+    class Meta:
+        model = Tag
+        fields = ['id', 'url', 'display', 'name', 'slug', 'color']

+ 5 - 5
netbox/tenancy/api/serializers.py

@@ -3,7 +3,7 @@ from drf_yasg.utils import swagger_serializer_method
 from rest_framework import serializers
 from rest_framework import serializers
 
 
 from netbox.api import ChoiceField, ContentTypeField
 from netbox.api import ChoiceField, ContentTypeField
-from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
+from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
 from tenancy.choices import ContactPriorityChoices
 from tenancy.choices import ContactPriorityChoices
 from tenancy.models import *
 from tenancy.models import *
 from utilities.api import get_serializer_for_model
 from utilities.api import get_serializer_for_model
@@ -27,7 +27,7 @@ class TenantGroupSerializer(NestedGroupModelSerializer):
         ]
         ]
 
 
 
 
-class TenantSerializer(PrimaryModelSerializer):
+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)
     circuit_count = serializers.IntegerField(read_only=True)
@@ -67,7 +67,7 @@ class ContactGroupSerializer(NestedGroupModelSerializer):
         ]
         ]
 
 
 
 
-class ContactRoleSerializer(PrimaryModelSerializer):
+class ContactRoleSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactrole-detail')
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactrole-detail')
 
 
     class Meta:
     class Meta:
@@ -77,7 +77,7 @@ class ContactRoleSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class ContactSerializer(PrimaryModelSerializer):
+class ContactSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contact-detail')
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contact-detail')
     group = NestedContactGroupSerializer(required=False, allow_null=True, default=None)
     group = NestedContactGroupSerializer(required=False, allow_null=True, default=None)
 
 
@@ -89,7 +89,7 @@ class ContactSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class ContactAssignmentSerializer(PrimaryModelSerializer):
+class ContactAssignmentSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactassignment-detail')
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactassignment-detail')
     content_type = ContentTypeField(
     content_type = ContentTypeField(
         queryset=ContentType.objects.all()
         queryset=ContentType.objects.all()

+ 6 - 6
netbox/virtualization/api/serializers.py

@@ -6,7 +6,7 @@ from dcim.choices import InterfaceModeChoices
 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 import ChoiceField, SerializedPKRelatedField
 from netbox.api import ChoiceField, SerializedPKRelatedField
-from netbox.api.serializers import PrimaryModelSerializer
+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 *
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
@@ -17,7 +17,7 @@ from .nested_serializers import *
 # Clusters
 # Clusters
 #
 #
 
 
-class ClusterTypeSerializer(PrimaryModelSerializer):
+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)
     cluster_count = serializers.IntegerField(read_only=True)
 
 
@@ -29,7 +29,7 @@ class ClusterTypeSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class ClusterGroupSerializer(PrimaryModelSerializer):
+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)
     cluster_count = serializers.IntegerField(read_only=True)
 
 
@@ -41,7 +41,7 @@ class ClusterGroupSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class ClusterSerializer(PrimaryModelSerializer):
+class ClusterSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
     url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
     type = NestedClusterTypeSerializer()
     type = NestedClusterTypeSerializer()
     group = NestedClusterGroupSerializer(required=False, allow_null=True, default=None)
     group = NestedClusterGroupSerializer(required=False, allow_null=True, default=None)
@@ -62,7 +62,7 @@ class ClusterSerializer(PrimaryModelSerializer):
 # Virtual machines
 # Virtual machines
 #
 #
 
 
-class VirtualMachineSerializer(PrimaryModelSerializer):
+class VirtualMachineSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
     url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
     status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
     status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
     site = NestedSiteSerializer(read_only=True)
     site = NestedSiteSerializer(read_only=True)
@@ -103,7 +103,7 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
 # VM interfaces
 # VM interfaces
 #
 #
 
 
-class VMInterfaceSerializer(PrimaryModelSerializer):
+class VMInterfaceSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
     url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
     virtual_machine = NestedVirtualMachineSerializer()
     virtual_machine = NestedVirtualMachineSerializer()
     parent = NestedVMInterfaceSerializer(required=False, allow_null=True)
     parent = NestedVMInterfaceSerializer(required=False, allow_null=True)

+ 3 - 3
netbox/wireless/api/serializers.py

@@ -4,7 +4,7 @@ from dcim.choices import LinkStatusChoices
 from dcim.api.serializers import NestedInterfaceSerializer
 from dcim.api.serializers import NestedInterfaceSerializer
 from ipam.api.serializers import NestedVLANSerializer
 from ipam.api.serializers import NestedVLANSerializer
 from netbox.api import ChoiceField
 from netbox.api import ChoiceField
-from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
+from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
 from wireless.choices import *
 from wireless.choices import *
 from wireless.models import *
 from wireless.models import *
 from .nested_serializers import *
 from .nested_serializers import *
@@ -29,7 +29,7 @@ class WirelessLANGroupSerializer(NestedGroupModelSerializer):
         ]
         ]
 
 
 
 
-class WirelessLANSerializer(PrimaryModelSerializer):
+class WirelessLANSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
     group = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
     group = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
     vlan = NestedVLANSerializer(required=False, allow_null=True)
     vlan = NestedVLANSerializer(required=False, allow_null=True)
@@ -44,7 +44,7 @@ class WirelessLANSerializer(PrimaryModelSerializer):
         ]
         ]
 
 
 
 
-class WirelessLinkSerializer(PrimaryModelSerializer):
+class WirelessLinkSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
     status = ChoiceField(choices=LinkStatusChoices, required=False)
     status = ChoiceField(choices=LinkStatusChoices, required=False)
     interface_a = NestedInterfaceSerializer()
     interface_a = NestedInterfaceSerializer()