Browse Source

Implemented static writable ModelSerializers for all models

Jeremy Stretch 8 years ago
parent
commit
bb1f97abc2

+ 22 - 13
netbox/circuits/api/serializers.py

@@ -2,7 +2,7 @@ from rest_framework import serializers
 
 
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
 from dcim.api.serializers import NestedSiteSerializer, DeviceInterfaceSerializer
 from dcim.api.serializers import NestedSiteSerializer, DeviceInterfaceSerializer
-from extras.api.serializers import CustomFieldSerializer
+from extras.api.serializers import CustomFieldValueSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 
 
 
 
@@ -10,17 +10,18 @@ from tenancy.api.serializers import NestedTenantSerializer
 # Providers
 # Providers
 #
 #
 
 
-class ProviderSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class ProviderSerializer(serializers.ModelSerializer):
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Provider
         model = Provider
         fields = [
         fields = [
             'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
             'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
-            'custom_fields',
+            'custom_field_values',
         ]
         ]
 
 
 
 
-class NestedProviderSerializer(serializers.HyperlinkedModelSerializer):
+class NestedProviderSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
 
 
     class Meta:
     class Meta:
@@ -28,6 +29,15 @@ class NestedProviderSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'name', 'slug']
         fields = ['id', 'url', 'name', 'slug']
 
 
 
 
+class WritableProviderSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = Provider
+        fields = [
+            'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
+        ]
+
+
 #
 #
 # Circuit types
 # Circuit types
 #
 #
@@ -39,7 +49,7 @@ class CircuitTypeSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug']
         fields = ['id', 'name', 'slug']
 
 
 
 
-class NestedCircuitTypeSerializer(serializers.HyperlinkedModelSerializer):
+class NestedCircuitTypeSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
 
 
     class Meta:
     class Meta:
@@ -64,20 +74,21 @@ class CircuitTerminationSerializer(serializers.ModelSerializer):
 # Circuits
 # Circuits
 #
 #
 
 
-class CircuitSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class CircuitSerializer(serializers.ModelSerializer):
     provider = NestedProviderSerializer()
     provider = NestedProviderSerializer()
     type = NestedCircuitTypeSerializer()
     type = NestedCircuitTypeSerializer()
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Circuit
         model = Circuit
         fields = [
         fields = [
             'id', 'cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
             'id', 'cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
-            'custom_fields',
+            'custom_field_values',
         ]
         ]
 
 
 
 
-class NestedCircuitSerializer(serializers.HyperlinkedModelSerializer):
+class NestedCircuitSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
     url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
 
 
     class Meta:
     class Meta:
@@ -85,12 +96,10 @@ class NestedCircuitSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'cid']
         fields = ['id', 'url', 'cid']
 
 
 
 
-# TODO: Delete this
-class CircuitDetailSerializer(CircuitSerializer):
-    terminations = CircuitTerminationSerializer(many=True)
+class WritableCircuitSerializer(serializers.ModelSerializer):
 
 
-    class Meta(CircuitSerializer.Meta):
+    class Meta:
+        model = Circuit
         fields = [
         fields = [
             'id', 'cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
             'id', 'cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
-            'terminations', 'custom_fields',
         ]
         ]

+ 0 - 3
netbox/circuits/api/urls.py

@@ -2,9 +2,6 @@ from django.conf.urls import include, url
 
 
 from rest_framework import routers
 from rest_framework import routers
 
 
-from extras.models import GRAPH_TYPE_PROVIDER
-from extras.api.views import GraphListView
-
 from . import views
 from . import views
 
 
 
 

+ 3 - 1
netbox/circuits/api/views.py

@@ -21,9 +21,10 @@ from . import serializers
 # Providers
 # Providers
 #
 #
 
 
-class ProviderViewSet(CustomFieldModelViewSet):
+class ProviderViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Provider.objects.all()
     queryset = Provider.objects.all()
     serializer_class = serializers.ProviderSerializer
     serializer_class = serializers.ProviderSerializer
+    write_serializer_class = serializers.WritableProviderSerializer
 
 
     @detail_route()
     @detail_route()
     def graphs(self, request, pk=None):
     def graphs(self, request, pk=None):
@@ -49,6 +50,7 @@ class CircuitTypeViewSet(ModelViewSet):
 class CircuitViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class CircuitViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Circuit.objects.select_related('type', 'tenant', 'provider')
     queryset = Circuit.objects.select_related('type', 'tenant', 'provider')
     serializer_class = serializers.CircuitSerializer
     serializer_class = serializers.CircuitSerializer
+    write_serializer_class = serializers.WritableCircuitSerializer
     filter_class = CircuitFilter
     filter_class = CircuitFilter
 
 
 
 

+ 90 - 55
netbox/dcim/api/serializers.py

@@ -4,10 +4,10 @@ from ipam.models import IPAddress
 from dcim.models import (
 from dcim.models import (
     ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, DeviceType,
     ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, DeviceType,
     DeviceRole, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer, Module, Platform, PowerOutlet,
     DeviceRole, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer, Module, Platform, PowerOutlet,
-    PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackRole, RACK_FACE_FRONT, RACK_FACE_REAR, Site,
-    SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT,
+    PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackRole, Site, SUBDEVICE_ROLE_CHILD,
+    SUBDEVICE_ROLE_PARENT,
 )
 )
-from extras.api.serializers import CustomFieldSerializer
+from extras.api.serializers import CustomFieldValueSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 
 
 
 
@@ -15,19 +15,20 @@ from tenancy.api.serializers import NestedTenantSerializer
 # Sites
 # Sites
 #
 #
 
 
-class SiteSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class SiteSerializer(serializers.ModelSerializer):
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Site
         model = Site
         fields = [
         fields = [
             'id', 'name', 'slug', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address', 'contact_name',
             'id', 'name', 'slug', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address', 'contact_name',
-            'contact_phone', 'contact_email', 'comments', 'custom_fields', 'count_prefixes', 'count_vlans',
+            'contact_phone', 'contact_email', 'comments', 'custom_field_values', 'count_prefixes', 'count_vlans',
             'count_racks', 'count_devices', 'count_circuits',
             'count_racks', 'count_devices', 'count_circuits',
         ]
         ]
 
 
 
 
-class NestedSiteSerializer(serializers.HyperlinkedModelSerializer):
+class NestedSiteSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
 
 
     class Meta:
     class Meta:
@@ -35,6 +36,16 @@ class NestedSiteSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'name', 'slug']
         fields = ['id', 'url', 'name', 'slug']
 
 
 
 
+class WritableSiteSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = Site
+        fields = [
+            'id', 'name', 'slug', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address', 'contact_name',
+            'contact_phone', 'contact_email', 'comments',
+        ]
+
+
 #
 #
 # Rack groups
 # Rack groups
 #
 #
@@ -47,7 +58,7 @@ class RackGroupSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug', 'site']
         fields = ['id', 'name', 'slug', 'site']
 
 
 
 
-class NestedRackGroupSerializer(serializers.HyperlinkedModelSerializer):
+class NestedRackGroupSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
 
 
     class Meta:
     class Meta:
@@ -55,6 +66,13 @@ class NestedRackGroupSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'name', 'slug']
         fields = ['id', 'url', 'name', 'slug']
 
 
 
 
+class WritableRackGroupSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = RackGroup
+        fields = ['id', 'name', 'slug', 'site']
+
+
 #
 #
 # Rack roles
 # Rack roles
 #
 #
@@ -66,7 +84,7 @@ class RackRoleSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug', 'color']
         fields = ['id', 'name', 'slug', 'color']
 
 
 
 
-class NestedRackRoleSerializer(serializers.HyperlinkedModelSerializer):
+class NestedRackRoleSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
 
 
     class Meta:
     class Meta:
@@ -79,21 +97,22 @@ class NestedRackRoleSerializer(serializers.HyperlinkedModelSerializer):
 #
 #
 
 
 
 
-class RackSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class RackSerializer(serializers.ModelSerializer):
     site = NestedSiteSerializer()
     site = NestedSiteSerializer()
     group = NestedRackGroupSerializer()
     group = NestedRackGroupSerializer()
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
     role = NestedRackRoleSerializer()
     role = NestedRackRoleSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Rack
         model = Rack
         fields = [
         fields = [
             'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height',
             'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height',
-            'desc_units', 'comments', 'custom_fields',
+            'desc_units', 'comments', 'custom_field_values',
         ]
         ]
 
 
 
 
-class NestedRackSerializer(serializers.HyperlinkedModelSerializer):
+class NestedRackSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
 
 
     class Meta:
     class Meta:
@@ -101,28 +120,15 @@ class NestedRackSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'name', 'display_name']
         fields = ['id', 'url', 'name', 'display_name']
 
 
 
 
-class RackDetailSerializer(RackSerializer):
-    front_units = serializers.SerializerMethodField()
-    rear_units = serializers.SerializerMethodField()
+class WritableRackSerializer(serializers.ModelSerializer):
 
 
-    class Meta(RackSerializer.Meta):
+    class Meta:
+        model = Rack
         fields = [
         fields = [
-            'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height',
-            'desc_units', 'comments', 'custom_fields', 'front_units', 'rear_units',
+            'id', 'name', 'facility_id', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height', 'desc_units',
+            'comments',
         ]
         ]
 
 
-    def get_front_units(self, obj):
-        units = obj.get_rack_units(face=RACK_FACE_FRONT)
-        for u in units:
-            u['device'] = NestedDeviceSerializer(u['device']).data if u['device'] else None
-        return units
-
-    def get_rear_units(self, obj):
-        units = obj.get_rack_units(face=RACK_FACE_REAR)
-        for u in units:
-            u['device'] = NestedDeviceSerializer(u['device']).data if u['device'] else None
-        return units
-
 
 
 #
 #
 # Manufacturers
 # Manufacturers
@@ -135,7 +141,7 @@ class ManufacturerSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug']
         fields = ['id', 'name', 'slug']
 
 
 
 
-class NestedManufacturerSerializer(serializers.HyperlinkedModelSerializer):
+class NestedManufacturerSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
 
 
     class Meta:
     class Meta:
@@ -147,16 +153,17 @@ class NestedManufacturerSerializer(serializers.HyperlinkedModelSerializer):
 # Device types
 # Device types
 #
 #
 
 
-class DeviceTypeSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class DeviceTypeSerializer(serializers.ModelSerializer):
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
     subdevice_role = serializers.SerializerMethodField()
     subdevice_role = serializers.SerializerMethodField()
     instance_count = serializers.IntegerField(source='instances.count', read_only=True)
     instance_count = serializers.IntegerField(source='instances.count', read_only=True)
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = DeviceType
         model = DeviceType
         fields = [
         fields = [
             'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
             'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
-            'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'custom_fields',
+            'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'custom_field_values',
             'instance_count',
             'instance_count',
         ]
         ]
 
 
@@ -168,7 +175,7 @@ class DeviceTypeSerializer(CustomFieldSerializer, serializers.ModelSerializer):
         }[obj.subdevice_role]
         }[obj.subdevice_role]
 
 
 
 
-class NestedDeviceTypeSerializer(serializers.HyperlinkedModelSerializer):
+class NestedDeviceTypeSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
 
 
@@ -177,6 +184,16 @@ class NestedDeviceTypeSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'manufacturer', 'model', 'slug']
         fields = ['id', 'url', 'manufacturer', 'model', 'slug']
 
 
 
 
+class WritableDeviceTypeSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = DeviceType
+        fields = [
+            'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
+            'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments',
+        ]
+
+
 class ConsolePortTemplateSerializer(serializers.ModelSerializer):
 class ConsolePortTemplateSerializer(serializers.ModelSerializer):
 
 
     class Meta:
     class Meta:
@@ -230,7 +247,7 @@ class DeviceRoleSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug', 'color']
         fields = ['id', 'name', 'slug', 'color']
 
 
 
 
-class NestedDeviceRoleSerializer(serializers.HyperlinkedModelSerializer):
+class NestedDeviceRoleSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
 
 
     class Meta:
     class Meta:
@@ -249,7 +266,7 @@ class PlatformSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug', 'rpc_client']
         fields = ['id', 'name', 'slug', 'rpc_client']
 
 
 
 
-class NestedPlatformSerializer(serializers.HyperlinkedModelSerializer):
+class NestedPlatformSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
 
 
     class Meta:
     class Meta:
@@ -262,7 +279,7 @@ class NestedPlatformSerializer(serializers.HyperlinkedModelSerializer):
 #
 #
 
 
 # Cannot import ipam.api.NestedIPAddressSerializer due to circular dependency
 # Cannot import ipam.api.NestedIPAddressSerializer due to circular dependency
-class DeviceIPAddressSerializer(serializers.HyperlinkedModelSerializer):
+class DeviceIPAddressSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
 
 
     class Meta:
     class Meta:
@@ -270,7 +287,7 @@ class DeviceIPAddressSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'family', 'address']
         fields = ['id', 'url', 'family', 'address']
 
 
 
 
-class DeviceSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class DeviceSerializer(serializers.ModelSerializer):
     device_type = NestedDeviceTypeSerializer()
     device_type = NestedDeviceTypeSerializer()
     device_role = NestedDeviceRoleSerializer()
     device_role = NestedDeviceRoleSerializer()
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
@@ -280,13 +297,14 @@ class DeviceSerializer(CustomFieldSerializer, serializers.ModelSerializer):
     primary_ip4 = DeviceIPAddressSerializer()
     primary_ip4 = DeviceIPAddressSerializer()
     primary_ip6 = DeviceIPAddressSerializer()
     primary_ip6 = DeviceIPAddressSerializer()
     parent_device = serializers.SerializerMethodField()
     parent_device = serializers.SerializerMethodField()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Device
         model = Device
         fields = [
         fields = [
             'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial',  'asset_tag',
             'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial',  'asset_tag',
             'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
             'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
-            'comments', 'custom_fields',
+            'comments', 'custom_field_values',
         ]
         ]
 
 
     def get_parent_device(self, obj):
     def get_parent_device(self, obj):
@@ -304,7 +322,7 @@ class DeviceSerializer(CustomFieldSerializer, serializers.ModelSerializer):
         }
         }
 
 
 
 
-class NestedDeviceSerializer(serializers.HyperlinkedModelSerializer):
+class NestedDeviceSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
 
 
     class Meta:
     class Meta:
@@ -312,19 +330,29 @@ class NestedDeviceSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'name', 'display_name']
         fields = ['id', 'url', 'name', 'display_name']
 
 
 
 
+class WritableDeviceSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = Device
+        fields = [
+            'id', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial',  'asset_tag', 'rack',
+            'position', 'face', 'status', 'primary_ip4', 'primary_ip6', 'comments',
+        ]
+
+
 #
 #
 # Console server ports
 # Console server ports
 #
 #
 
 
 class ConsoleServerPortSerializer(serializers.ModelSerializer):
 class ConsoleServerPortSerializer(serializers.ModelSerializer):
-    device = NestedDeviceSerializer()
+    device = NestedDeviceSerializer(read_only=True)
 
 
     class Meta:
     class Meta:
         model = ConsoleServerPort
         model = ConsoleServerPort
         fields = ['id', 'device', 'name', 'connected_console']
         fields = ['id', 'device', 'name', 'connected_console']
 
 
 
 
-class DeviceConsoleServerPortSerializer(serializers.HyperlinkedModelSerializer):
+class DeviceConsoleServerPortSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
 
 
     class Meta:
     class Meta:
@@ -338,7 +366,7 @@ class DeviceConsoleServerPortSerializer(serializers.HyperlinkedModelSerializer):
 #
 #
 
 
 class ConsolePortSerializer(serializers.ModelSerializer):
 class ConsolePortSerializer(serializers.ModelSerializer):
-    device = NestedDeviceSerializer()
+    device = NestedDeviceSerializer(read_only=True)
     cs_port = ConsoleServerPortSerializer()
     cs_port = ConsoleServerPortSerializer()
 
 
     class Meta:
     class Meta:
@@ -346,7 +374,7 @@ class ConsolePortSerializer(serializers.ModelSerializer):
         fields = ['id', 'device', 'name', 'cs_port', 'connection_status']
         fields = ['id', 'device', 'name', 'cs_port', 'connection_status']
 
 
 
 
-class DeviceConsolePortSerializer(serializers.HyperlinkedModelSerializer):
+class DeviceConsolePortSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
 
 
     class Meta:
     class Meta:
@@ -360,14 +388,14 @@ class DeviceConsolePortSerializer(serializers.HyperlinkedModelSerializer):
 #
 #
 
 
 class PowerOutletSerializer(serializers.ModelSerializer):
 class PowerOutletSerializer(serializers.ModelSerializer):
-    device = NestedDeviceSerializer()
+    device = NestedDeviceSerializer(read_only=True)
 
 
     class Meta:
     class Meta:
         model = PowerOutlet
         model = PowerOutlet
         fields = ['id', 'device', 'name', 'connected_port']
         fields = ['id', 'device', 'name', 'connected_port']
 
 
 
 
-class DevicePowerOutletSerializer(serializers.HyperlinkedModelSerializer):
+class DevicePowerOutletSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
 
 
     class Meta:
     class Meta:
@@ -381,7 +409,7 @@ class DevicePowerOutletSerializer(serializers.HyperlinkedModelSerializer):
 #
 #
 
 
 class PowerPortSerializer(serializers.ModelSerializer):
 class PowerPortSerializer(serializers.ModelSerializer):
-    device = NestedDeviceSerializer()
+    device = NestedDeviceSerializer(read_only=True)
     power_outlet = PowerOutletSerializer()
     power_outlet = PowerOutletSerializer()
 
 
     class Meta:
     class Meta:
@@ -389,7 +417,7 @@ class PowerPortSerializer(serializers.ModelSerializer):
         fields = ['id', 'device', 'name', 'power_outlet', 'connection_status']
         fields = ['id', 'device', 'name', 'power_outlet', 'connection_status']
 
 
 
 
-class DevicePowerPortSerializer(serializers.HyperlinkedModelSerializer):
+class DevicePowerPortSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
 
 
     class Meta:
     class Meta:
@@ -404,7 +432,7 @@ class DevicePowerPortSerializer(serializers.HyperlinkedModelSerializer):
 
 
 
 
 class InterfaceSerializer(serializers.ModelSerializer):
 class InterfaceSerializer(serializers.ModelSerializer):
-    device = NestedDeviceSerializer()
+    device = NestedDeviceSerializer(read_only=True)
     connection = serializers.SerializerMethodField(read_only=True)
     connection = serializers.SerializerMethodField(read_only=True)
     connected_interface = serializers.SerializerMethodField(read_only=True)
     connected_interface = serializers.SerializerMethodField(read_only=True)
 
 
@@ -426,7 +454,7 @@ class InterfaceSerializer(serializers.ModelSerializer):
         return None
         return None
 
 
 
 
-class PeerInterfaceSerializer(serializers.HyperlinkedModelSerializer):
+class PeerInterfaceSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
     device = NestedDeviceSerializer()
     device = NestedDeviceSerializer()
 
 
@@ -435,7 +463,7 @@ class PeerInterfaceSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'device', 'name', 'form_factor', 'mac_address', 'mgmt_only', 'description']
         fields = ['id', 'url', 'device', 'name', 'form_factor', 'mac_address', 'mgmt_only', 'description']
 
 
 
 
-class DeviceInterfaceSerializer(serializers.HyperlinkedModelSerializer):
+class DeviceInterfaceSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
     connection = serializers.SerializerMethodField()
     connection = serializers.SerializerMethodField()
 
 
@@ -462,7 +490,7 @@ class InterfaceConnectionSerializer(serializers.ModelSerializer):
         fields = ['id', 'interface_a', 'interface_b', 'connection_status']
         fields = ['id', 'interface_a', 'interface_b', 'connection_status']
 
 
 
 
-class NestedInterfaceConnectionSerializer(serializers.HyperlinkedModelSerializer):
+class NestedInterfaceConnectionSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfaceconnection-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfaceconnection-detail')
 
 
     class Meta:
     class Meta:
@@ -470,12 +498,19 @@ class NestedInterfaceConnectionSerializer(serializers.HyperlinkedModelSerializer
         fields = ['id', 'url', 'connection_status']
         fields = ['id', 'url', 'connection_status']
 
 
 
 
+class WritableInterfaceConnectionSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = InterfaceConnection
+        fields = ['id', 'interface_a', 'interface_b', 'connection_status']
+
+
 #
 #
 # Device bays
 # Device bays
 #
 #
 
 
 class DeviceBaySerializer(serializers.ModelSerializer):
 class DeviceBaySerializer(serializers.ModelSerializer):
-    device = NestedDeviceSerializer()
+    device = NestedDeviceSerializer(read_only=True)
     installed_device = NestedDeviceSerializer()
     installed_device = NestedDeviceSerializer()
 
 
     class Meta:
     class Meta:
@@ -483,7 +518,7 @@ class DeviceBaySerializer(serializers.ModelSerializer):
         fields = ['id', 'device', 'name', 'installed_device']
         fields = ['id', 'device', 'name', 'installed_device']
 
 
 
 
-class DeviceDeviceBaySerializer(serializers.HyperlinkedModelSerializer):
+class DeviceDeviceBaySerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
 
 
     class Meta:
     class Meta:
@@ -497,7 +532,7 @@ class DeviceDeviceBaySerializer(serializers.HyperlinkedModelSerializer):
 #
 #
 
 
 class ModuleSerializer(serializers.ModelSerializer):
 class ModuleSerializer(serializers.ModelSerializer):
-    device = NestedDeviceSerializer()
+    device = NestedDeviceSerializer(read_only=True)
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
 
 
     class Meta:
     class Meta:
@@ -505,7 +540,7 @@ class ModuleSerializer(serializers.ModelSerializer):
         fields = ['id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'discovered']
         fields = ['id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'discovered']
 
 
 
 
-class DeviceModuleSerializer(serializers.HyperlinkedModelSerializer):
+class DeviceModuleSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
     manufacturer = NestedManufacturerSerializer()
     manufacturer = NestedManufacturerSerializer()
 
 

+ 1 - 3
netbox/dcim/api/urls.py

@@ -2,10 +2,8 @@ from django.conf.urls import include, url
 
 
 from rest_framework import routers
 from rest_framework import routers
 
 
-from extras.models import GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
-from extras.api.views import GraphListView, TopologyMapView
+from extras.api.views import TopologyMapView
 from ipam.api.views import ServiceViewSet, DeviceServiceViewSet
 from ipam.api.views import ServiceViewSet, DeviceServiceViewSet
-
 from . import views
 from . import views
 
 
 
 

+ 7 - 1
netbox/dcim/api/views.py

@@ -33,6 +33,7 @@ from . import serializers
 class SiteViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class SiteViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Site.objects.select_related('tenant')
     queryset = Site.objects.select_related('tenant')
     serializer_class = serializers.SiteSerializer
     serializer_class = serializers.SiteSerializer
+    write_serializer_class = serializers.WritableSiteSerializer
 
 
     @detail_route()
     @detail_route()
     def graphs(self, request, pk=None):
     def graphs(self, request, pk=None):
@@ -50,6 +51,7 @@ class RackGroupViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = RackGroup.objects.select_related('site')
     queryset = RackGroup.objects.select_related('site')
     serializer_class = serializers.RackGroupSerializer
     serializer_class = serializers.RackGroupSerializer
     filter_class = filters.RackGroupFilter
     filter_class = filters.RackGroupFilter
+    write_serializer_class = serializers.WritableRackGroupSerializer
 
 
 
 
 #
 #
@@ -68,6 +70,7 @@ class RackRoleViewSet(ModelViewSet):
 class RackViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class RackViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Rack.objects.select_related('site', 'group__site', 'tenant')
     queryset = Rack.objects.select_related('site', 'group__site', 'tenant')
     serializer_class = serializers.RackSerializer
     serializer_class = serializers.RackSerializer
+    write_serializer_class = serializers.WritableRackSerializer
     filter_class = filters.RackFilter
     filter_class = filters.RackFilter
 
 
     @detail_route(url_path='rack-units')
     @detail_route(url_path='rack-units')
@@ -112,6 +115,7 @@ class ManufacturerViewSet(ModelViewSet):
 class DeviceTypeViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class DeviceTypeViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = DeviceType.objects.select_related('manufacturer')
     queryset = DeviceType.objects.select_related('manufacturer')
     serializer_class = serializers.DeviceTypeSerializer
     serializer_class = serializers.DeviceTypeSerializer
+    write_serializer_class = serializers.WritableDeviceTypeSerializer
 
 
 
 
 #
 #
@@ -143,6 +147,7 @@ class DeviceViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
         'primary_ip4__nat_outside', 'primary_ip6__nat_outside',
         'primary_ip4__nat_outside', 'primary_ip6__nat_outside',
     )
     )
     serializer_class = serializers.DeviceSerializer
     serializer_class = serializers.DeviceSerializer
+    write_serializer_class = serializers.WritableDeviceSerializer
     filter_class = filters.DeviceFilter
     filter_class = filters.DeviceFilter
     renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
     renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
 
 
@@ -335,9 +340,10 @@ class DeviceModuleViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
 # Interface connections
 # Interface connections
 #
 #
 
 
-class InterfaceConnectionViewSet(ModelViewSet):
+class InterfaceConnectionViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = InterfaceConnection.objects.select_related('interface_a__device', 'interface_b__device')
     queryset = InterfaceConnection.objects.select_related('interface_a__device', 'interface_b__device')
     serializer_class = serializers.InterfaceConnectionSerializer
     serializer_class = serializers.InterfaceConnectionSerializer
+    write_serializer_class = serializers.WritableInterfaceConnectionSerializer
 
 
 
 
 #
 #

+ 36 - 29
netbox/extras/api/serializers.py

@@ -1,35 +1,42 @@
 from rest_framework import serializers
 from rest_framework import serializers
 
 
-from extras.models import CF_TYPE_SELECT, CustomFieldChoice, Graph
+from extras.models import CF_TYPE_SELECT, CustomFieldChoice, CustomFieldValue, Graph
+
+
+# class CustomFieldSerializer(serializers.ModelSerializer):
+#     """
+#     Extends ModelSerializer to render any CustomFields and their values associated with an object.
+#     """
+#     custom_fields = serializers.SerializerMethodField()
+#
+#     def get_custom_fields(self, obj):
+#
+#         # Gather all CustomFields applicable to this object
+#         fields = {cf.name: None for cf in self.context['custom_fields']}
+#         custom_field_choices = self.context['custom_field_choices']
+#
+#         # Attach any defined CustomFieldValues to their respective CustomFields
+#         for cfv in obj.custom_field_values.all():
+#
+#             # Attempt to suppress database lookups for CustomFieldChoices by using the cached choice set from the view
+#             # context.
+#             if cfv.field.type == CF_TYPE_SELECT:
+#                 cfc = {
+#                     'id': int(cfv.serialized_value),
+#                     'value': custom_field_choices[int(cfv.serialized_value)]
+#                 }
+#                 fields[cfv.field.name] = CustomFieldChoiceSerializer(instance=cfc).data
+#             else:
+#                 fields[cfv.field.name] = cfv.value
+#
+#         return fields
+
+
+class CustomFieldValueSerializer(serializers.ModelSerializer):
 
 
-
-class CustomFieldSerializer(serializers.Serializer):
-    """
-    Extends a ModelSerializer to render any CustomFields and their values associated with an object.
-    """
-    custom_fields = serializers.SerializerMethodField()
-
-    def get_custom_fields(self, obj):
-
-        # Gather all CustomFields applicable to this object
-        fields = {cf.name: None for cf in self.context['custom_fields']}
-        custom_field_choices = self.context['custom_field_choices']
-
-        # Attach any defined CustomFieldValues to their respective CustomFields
-        for cfv in obj.custom_field_values.all():
-
-            # Attempt to suppress database lookups for CustomFieldChoices by using the cached choice set from the view
-            # context.
-            if cfv.field.type == CF_TYPE_SELECT:
-                cfc = {
-                    'id': int(cfv.serialized_value),
-                    'value': custom_field_choices[int(cfv.serialized_value)]
-                }
-                fields[cfv.field.name] = CustomFieldChoiceSerializer(instance=cfc).data
-            else:
-                fields[cfv.field.name] = cfv.value
-
-        return fields
+    class Meta:
+        model = CustomFieldValue
+        fields = ['field', 'serialized_value']
 
 
 
 
 class CustomFieldChoiceSerializer(serializers.ModelSerializer):
 class CustomFieldChoiceSerializer(serializers.ModelSerializer):

+ 72 - 22
netbox/ipam/api/serializers.py

@@ -1,25 +1,25 @@
 from rest_framework import serializers
 from rest_framework import serializers
 
 
 from dcim.api.serializers import NestedDeviceSerializer, DeviceInterfaceSerializer, NestedSiteSerializer
 from dcim.api.serializers import NestedDeviceSerializer, DeviceInterfaceSerializer, NestedSiteSerializer
-from extras.api.serializers import CustomFieldSerializer
+from extras.api.serializers import CustomFieldValueSerializer
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from tenancy.api.serializers import NestedTenantSerializer
 from tenancy.api.serializers import NestedTenantSerializer
-from utilities.api import WritableSerializerMixin
 
 
 
 
 #
 #
 # VRFs
 # VRFs
 #
 #
 
 
-class VRFSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class VRFSerializer(serializers.ModelSerializer):
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = VRF
         model = VRF
-        fields = ['id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'custom_fields']
+        fields = ['id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'custom_field_values']
 
 
 
 
-class NestedVRFSerializer(serializers.HyperlinkedModelSerializer):
+class NestedVRFSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
 
 
     class Meta:
     class Meta:
@@ -27,6 +27,13 @@ class NestedVRFSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'name', 'rd']
         fields = ['id', 'url', 'name', 'rd']
 
 
 
 
+class WritableVRFSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = VRF
+        fields = ['id', 'name', 'rd', 'tenant', 'enforce_unique', 'description']
+
+
 #
 #
 # Roles
 # Roles
 #
 #
@@ -38,7 +45,7 @@ class RoleSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug', 'weight']
         fields = ['id', 'name', 'slug', 'weight']
 
 
 
 
-class NestedRoleSerializer(serializers.HyperlinkedModelSerializer):
+class NestedRoleSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
 
 
     class Meta:
     class Meta:
@@ -57,7 +64,7 @@ class RIRSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug', 'is_private']
         fields = ['id', 'name', 'slug', 'is_private']
 
 
 
 
-class NestedRIRSerializer(serializers.HyperlinkedModelSerializer):
+class NestedRIRSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
 
 
     class Meta:
     class Meta:
@@ -69,15 +76,16 @@ class NestedRIRSerializer(serializers.HyperlinkedModelSerializer):
 # Aggregates
 # Aggregates
 #
 #
 
 
-class AggregateSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class AggregateSerializer(serializers.ModelSerializer):
     rir = NestedRIRSerializer()
     rir = NestedRIRSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Aggregate
         model = Aggregate
-        fields = ['id', 'family', 'prefix', 'rir', 'date_added', 'description', 'custom_fields']
+        fields = ['id', 'family', 'prefix', 'rir', 'date_added', 'description', 'custom_field_values']
 
 
 
 
-class NestedAggregateSerializer(serializers.HyperlinkedModelSerializer):
+class NestedAggregateSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
 
 
     class Meta(AggregateSerializer.Meta):
     class Meta(AggregateSerializer.Meta):
@@ -85,11 +93,18 @@ class NestedAggregateSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'family', 'prefix']
         fields = ['id', 'url', 'family', 'prefix']
 
 
 
 
+class WritableAggregateSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = Aggregate
+        fields = ['id', 'family', 'prefix', 'rir', 'date_added', 'description']
+
+
 #
 #
 # VLAN groups
 # VLAN groups
 #
 #
 
 
-class VLANGroupSerializer(WritableSerializerMixin, serializers.ModelSerializer):
+class VLANGroupSerializer(serializers.ModelSerializer):
     site = NestedSiteSerializer()
     site = NestedSiteSerializer()
 
 
     class Meta:
     class Meta:
@@ -97,7 +112,7 @@ class VLANGroupSerializer(WritableSerializerMixin, serializers.ModelSerializer):
         fields = ['id', 'name', 'slug', 'site']
         fields = ['id', 'name', 'slug', 'site']
 
 
 
 
-class NestedVLANGroupSerializer(serializers.HyperlinkedModelSerializer):
+class NestedVLANGroupSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
 
 
     class Meta:
     class Meta:
@@ -105,25 +120,33 @@ class NestedVLANGroupSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'name', 'slug']
         fields = ['id', 'url', 'name', 'slug']
 
 
 
 
+class WritableVLANGroupSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = VLANGroup
+        fields = ['id', 'name', 'slug', 'site']
+
+
 #
 #
 # VLANs
 # VLANs
 #
 #
 
 
-class VLANSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class VLANSerializer(serializers.ModelSerializer):
     site = NestedSiteSerializer()
     site = NestedSiteSerializer()
     group = NestedVLANGroupSerializer()
     group = NestedVLANGroupSerializer()
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
     role = NestedRoleSerializer()
     role = NestedRoleSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = VLAN
         model = VLAN
         fields = [
         fields = [
             'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'display_name',
             'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'display_name',
-            'custom_fields',
+            'custom_field_values',
         ]
         ]
 
 
 
 
-class NestedVLANSerializer(serializers.HyperlinkedModelSerializer):
+class NestedVLANSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
 
 
     class Meta:
     class Meta:
@@ -131,26 +154,36 @@ class NestedVLANSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'vid', 'name', 'display_name']
         fields = ['id', 'url', 'vid', 'name', 'display_name']
 
 
 
 
+class WritableVLANSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = VLAN
+        fields = [
+            'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description',
+        ]
+
+
 #
 #
 # Prefixes
 # Prefixes
 #
 #
 
 
-class PrefixSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class PrefixSerializer(serializers.ModelSerializer):
     site = NestedSiteSerializer()
     site = NestedSiteSerializer()
     vrf = NestedVRFSerializer()
     vrf = NestedVRFSerializer()
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
     vlan = NestedVLANSerializer()
     vlan = NestedVLANSerializer()
     role = NestedRoleSerializer()
     role = NestedRoleSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Prefix
         model = Prefix
         fields = [
         fields = [
             'id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
             'id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
-            'custom_fields',
+            'custom_field_values',
         ]
         ]
 
 
 
 
-class NestedPrefixSerializer(serializers.HyperlinkedModelSerializer):
+class NestedPrefixSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
 
 
     class Meta:
     class Meta:
@@ -158,24 +191,34 @@ class NestedPrefixSerializer(serializers.HyperlinkedModelSerializer):
         fields = ['id', 'url', 'family', 'prefix']
         fields = ['id', 'url', 'family', 'prefix']
 
 
 
 
+class WritablePrefixSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = Prefix
+        fields = [
+            'id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
+        ]
+
+
 #
 #
 # IP addresses
 # IP addresses
 #
 #
 
 
-class IPAddressSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class IPAddressSerializer(serializers.ModelSerializer):
     vrf = NestedVRFSerializer()
     vrf = NestedVRFSerializer()
     tenant = NestedTenantSerializer()
     tenant = NestedTenantSerializer()
     interface = DeviceInterfaceSerializer()
     interface = DeviceInterfaceSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = IPAddress
         model = IPAddress
         fields = [
         fields = [
             'id', 'family', 'address', 'vrf', 'tenant', 'status', 'interface', 'description', 'nat_inside',
             'id', 'family', 'address', 'vrf', 'tenant', 'status', 'interface', 'description', 'nat_inside',
-            'nat_outside', 'custom_fields',
+            'nat_outside', 'custom_field_values',
         ]
         ]
 
 
 
 
-class NestedIPAddressSerializer(serializers.HyperlinkedModelSerializer):
+class NestedIPAddressSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
 
 
     class Meta:
     class Meta:
@@ -186,6 +229,13 @@ IPAddressSerializer._declared_fields['nat_inside'] = NestedIPAddressSerializer()
 IPAddressSerializer._declared_fields['nat_outside'] = NestedIPAddressSerializer()
 IPAddressSerializer._declared_fields['nat_outside'] = NestedIPAddressSerializer()
 
 
 
 
+class WritableIPAddressSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = IPAddress
+        fields = ['id', 'family', 'address', 'vrf', 'tenant', 'status', 'interface', 'description', 'nat_inside']
+
+
 #
 #
 # Services
 # Services
 #
 #
@@ -199,7 +249,7 @@ class ServiceSerializer(serializers.ModelSerializer):
         fields = ['id', 'device', 'name', 'port', 'protocol', 'ipaddresses', 'description']
         fields = ['id', 'device', 'name', 'port', 'protocol', 'ipaddresses', 'description']
 
 
 
 
-class DeviceServiceSerializer(serializers.HyperlinkedModelSerializer):
+class DeviceServiceSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
     ipaddresses = NestedIPAddressSerializer(many=True)
     ipaddresses = NestedIPAddressSerializer(many=True)
 
 

+ 6 - 0
netbox/ipam/api/views.py

@@ -20,6 +20,7 @@ from . import serializers
 class VRFViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class VRFViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = VRF.objects.select_related('tenant')
     queryset = VRF.objects.select_related('tenant')
     serializer_class = serializers.VRFSerializer
     serializer_class = serializers.VRFSerializer
+    write_serializer_class = serializers.WritableVRFSerializer
     filter_class = filters.VRFFilter
     filter_class = filters.VRFFilter
 
 
 
 
@@ -48,6 +49,7 @@ class RIRViewSet(ModelViewSet):
 class AggregateViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class AggregateViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Aggregate.objects.select_related('rir')
     queryset = Aggregate.objects.select_related('rir')
     serializer_class = serializers.AggregateSerializer
     serializer_class = serializers.AggregateSerializer
+    write_serializer_class = serializers.WritableAggregateSerializer
     filter_class = filters.AggregateFilter
     filter_class = filters.AggregateFilter
 
 
 
 
@@ -58,6 +60,7 @@ class AggregateViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
     queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
     serializer_class = serializers.PrefixSerializer
     serializer_class = serializers.PrefixSerializer
+    write_serializer_class = serializers.WritablePrefixSerializer
     filter_class = filters.PrefixFilter
     filter_class = filters.PrefixFilter
 
 
 
 
@@ -68,6 +71,7 @@ class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')
     queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')
     serializer_class = serializers.IPAddressSerializer
     serializer_class = serializers.IPAddressSerializer
+    write_serializer_class = serializers.WritableIPAddressSerializer
     filter_class = filters.IPAddressFilter
     filter_class = filters.IPAddressFilter
 
 
 
 
@@ -78,6 +82,7 @@ class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class VLANGroupViewSet(WritableSerializerMixin, ModelViewSet):
 class VLANGroupViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = VLANGroup.objects.select_related('site')
     queryset = VLANGroup.objects.select_related('site')
     serializer_class = serializers.VLANGroupSerializer
     serializer_class = serializers.VLANGroupSerializer
+    write_serializer_class = serializers.WritableVLANGroupSerializer
     filter_class = filters.VLANGroupFilter
     filter_class = filters.VLANGroupFilter
 
 
 
 
@@ -88,6 +93,7 @@ class VLANGroupViewSet(WritableSerializerMixin, ModelViewSet):
 class VLANViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class VLANViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
     queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
     serializer_class = serializers.VLANSerializer
     serializer_class = serializers.VLANSerializer
+    write_serializer_class = serializers.WritableVLANSerializer
     filter_class = filters.VLANFilter
     filter_class = filters.VLANFilter
 
 
 
 

+ 1 - 1
netbox/secrets/api/serializers.py

@@ -15,7 +15,7 @@ class SecretRoleSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug']
         fields = ['id', 'name', 'slug']
 
 
 
 
-class NestedSecretRoleSerializer(serializers.HyperlinkedModelSerializer):
+class NestedSecretRoleSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='secrets-api:secretrole-detail')
     url = serializers.HyperlinkedIdentityField(view_name='secrets-api:secretrole-detail')
 
 
     class Meta:
     class Meta:

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

@@ -1,6 +1,6 @@
 from rest_framework import serializers
 from rest_framework import serializers
 
 
-from extras.api.serializers import CustomFieldSerializer
+from extras.api.serializers import CustomFieldValueSerializer
 from tenancy.models import Tenant, TenantGroup
 from tenancy.models import Tenant, TenantGroup
 
 
 
 
@@ -15,7 +15,7 @@ class TenantGroupSerializer(serializers.ModelSerializer):
         fields = ['id', 'name', 'slug']
         fields = ['id', 'name', 'slug']
 
 
 
 
-class NestedTenantGroupSerializer(serializers.HyperlinkedModelSerializer):
+class NestedTenantGroupSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
 
 
     class Meta:
     class Meta:
@@ -27,17 +27,25 @@ class NestedTenantGroupSerializer(serializers.HyperlinkedModelSerializer):
 # Tenants
 # Tenants
 #
 #
 
 
-class TenantSerializer(CustomFieldSerializer, serializers.ModelSerializer):
+class TenantSerializer(serializers.ModelSerializer):
     group = NestedTenantGroupSerializer()
     group = NestedTenantGroupSerializer()
+    custom_field_values = CustomFieldValueSerializer(many=True)
 
 
     class Meta:
     class Meta:
         model = Tenant
         model = Tenant
-        fields = ['id', 'name', 'slug', 'group', 'description', 'comments', 'custom_fields']
+        fields = ['id', 'name', 'slug', 'group', 'description', 'comments', 'custom_field_values']
 
 
 
 
-class NestedTenantSerializer(serializers.HyperlinkedModelSerializer):
+class NestedTenantSerializer(serializers.ModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
     url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
 
 
     class Meta:
     class Meta:
         model = Tenant
         model = Tenant
         fields = ['id', 'url', 'name', 'slug']
         fields = ['id', 'url', 'name', 'slug']
+
+
+class WritableTenantSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = Tenant
+        fields = ['id', 'name', 'slug', 'group', 'description', 'comments']

+ 1 - 0
netbox/tenancy/api/views.py

@@ -24,4 +24,5 @@ class TenantGroupViewSet(ModelViewSet):
 class TenantViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 class TenantViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Tenant.objects.select_related('group')
     queryset = Tenant.objects.select_related('group')
     serializer_class = serializers.TenantSerializer
     serializer_class = serializers.TenantSerializer
+    write_serializer_class = serializers.WritableTenantSerializer
     filter_class = TenantFilter
     filter_class = TenantFilter

+ 3 - 11
netbox/utilities/api.py

@@ -12,18 +12,10 @@ class ServiceUnavailable(APIException):
 
 
 class WritableSerializerMixin(object):
 class WritableSerializerMixin(object):
     """
     """
-    Returns a flat Serializer from the given model suitable for write operations (POST, PUT, PATCH). This is necessary
-    to allow write operations on objects which utilize nested serializers.
+    Allow for the use of an alternate, writable serializer class for write operations (e.g. POST, PUT).
     """
     """
 
 
     def get_serializer_class(self):
     def get_serializer_class(self):
-
-        class WritableSerializer(ModelSerializer):
-
-            class Meta(self.serializer_class.Meta):
-                pass
-
-        if self.action in WRITE_OPERATIONS:
-            return WritableSerializer
-
+        if self.action in WRITE_OPERATIONS and hasattr(self, 'write_serializer_class'):
+            return self.write_serializer_class
         return self.serializer_class
         return self.serializer_class