Parcourir la source

Introduced WritableSerializerMixin

Jeremy Stretch il y a 8 ans
Parent
commit
12d263999b

+ 6 - 8
netbox/circuits/api/views.py

@@ -9,6 +9,7 @@ from circuits.models import Provider, CircuitTermination, CircuitType, Circuit
 from circuits.filters import CircuitFilter
 
 from extras.api.views import CustomFieldModelViewSet
+from utilities.api import WritableSerializerMixin
 from . import serializers
 
 
@@ -34,26 +35,23 @@ class CircuitTypeViewSet(ModelViewSet):
 # Circuits
 #
 
-class CircuitViewSet(CustomFieldModelViewSet):
+class CircuitViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Circuit.objects.select_related('type', 'tenant', 'provider')
+    serializer_class = serializers.CircuitSerializer
     filter_class = CircuitFilter
 
-    def get_serializer_class(self):
-        if self.action == 'retrieve':
-            return serializers.CircuitDetailSerializer
-        return serializers.CircuitSerializer
-
 
 #
 # Circuit Terminations
 #
 
-class CircuitTerminationViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class CircuitTerminationViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin,
+                                GenericViewSet):
     queryset = CircuitTermination.objects.select_related('site', 'interface__device')
     serializer_class = serializers.CircuitTerminationSerializer
 
 
-class NestedCircuitTerminationViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class NestedCircuitTerminationViewSet(CreateModelMixin, ListModelMixin ,WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.CircuitTerminationSerializer
 
     def get_queryset(self):

+ 26 - 20
netbox/dcim/api/views.py

@@ -18,7 +18,7 @@ from dcim.models import (
 from dcim import filters
 from extras.api.views import CustomFieldModelViewSet
 from extras.api.renderers import BINDZoneRenderer, FlatJSONRenderer
-from utilities.api import ServiceUnavailable
+from utilities.api import ServiceUnavailable, WritableSerializerMixin
 from .exceptions import MissingFilterException
 from . import serializers
 
@@ -27,7 +27,7 @@ from . import serializers
 # Sites
 #
 
-class SiteViewSet(CustomFieldModelViewSet):
+class SiteViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Site.objects.select_related('tenant')
     serializer_class = serializers.SiteSerializer
 
@@ -36,7 +36,7 @@ class SiteViewSet(CustomFieldModelViewSet):
 # Rack groups
 #
 
-class RackGroupViewSet(ModelViewSet):
+class RackGroupViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = RackGroup.objects.select_related('site')
     serializer_class = serializers.RackGroupSerializer
     filter_class = filters.RackGroupFilter
@@ -55,7 +55,7 @@ class RackRoleViewSet(ModelViewSet):
 # Racks
 #
 
-class RackViewSet(CustomFieldModelViewSet):
+class RackViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Rack.objects.select_related('site', 'group__site', 'tenant')
     serializer_class = serializers.RackSerializer
     filter_class = filters.RackFilter
@@ -102,7 +102,7 @@ class ManufacturerViewSet(ModelViewSet):
 # Device Types
 #
 
-class DeviceTypeViewSet(CustomFieldModelViewSet):
+class DeviceTypeViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = DeviceType.objects.select_related('manufacturer')
     serializer_class = serializers.DeviceTypeSerializer
 
@@ -129,7 +129,7 @@ class PlatformViewSet(ModelViewSet):
 # Devices
 #
 
-class DeviceViewSet(CustomFieldModelViewSet):
+class DeviceViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Device.objects.select_related(
         'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'rack__site', 'parent_bay',
     ).prefetch_related(
@@ -144,12 +144,13 @@ class DeviceViewSet(CustomFieldModelViewSet):
 # Console Ports
 #
 
-class ConsolePortViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class ConsolePortViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin,
+                         GenericViewSet):
     queryset = ConsolePort.objects.select_related('cs_port')
     serializer_class = serializers.ConsolePortSerializer
 
 
-class ChildConsolePortViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class ChildConsolePortViewSet(CreateModelMixin, ListModelMixin, WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.ChildConsoleServerPortSerializer
 
     def get_queryset(self):
@@ -161,12 +162,13 @@ class ChildConsolePortViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
 # Console Server Ports
 #
 
-class ConsoleServerPortViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class ConsoleServerPortViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin,
+                               GenericViewSet):
     queryset = ConsoleServerPort.objects.select_related('connected_console')
     serializer_class = serializers.ConsoleServerPortSerializer
 
 
-class ChildConsoleServerPortViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class ChildConsoleServerPortViewSet(CreateModelMixin, ListModelMixin, WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.ChildConsoleServerPortSerializer
 
     def get_queryset(self):
@@ -178,12 +180,13 @@ class ChildConsoleServerPortViewSet(CreateModelMixin, ListModelMixin, GenericVie
 # Power Ports
 #
 
-class PowerPortViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class PowerPortViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin,
+                       GenericViewSet):
     queryset = PowerPort.objects.select_related('power_outlet')
     serializer_class = serializers.PowerPortSerializer
 
 
-class NestedPowerPortViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class NestedPowerPortViewSet(CreateModelMixin, ListModelMixin, WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.ChildPowerPortSerializer
 
     def get_queryset(self):
@@ -195,12 +198,13 @@ class NestedPowerPortViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
 # Power Outlets
 #
 
-class PowerOutletViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class PowerOutletViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin,
+                         GenericViewSet):
     queryset = PowerOutlet.objects.select_related('connected_port')
     serializer_class = serializers.PowerOutletSerializer
 
 
-class NestedPowerOutletViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class NestedPowerOutletViewSet(CreateModelMixin, ListModelMixin, WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.ChildPowerOutletSerializer
 
     def get_queryset(self):
@@ -212,12 +216,13 @@ class NestedPowerOutletViewSet(CreateModelMixin, ListModelMixin, GenericViewSet)
 # Interfaces
 #
 
-class InterfaceViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class InterfaceViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin,
+                       GenericViewSet):
     queryset = Interface.objects.select_related('device')
     serializer_class = serializers.InterfaceDetailSerializer
 
 
-class NestedInterfaceViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class NestedInterfaceViewSet(CreateModelMixin, ListModelMixin, WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.ChildInterfaceSerializer
     filter_class = filters.InterfaceFilter
 
@@ -231,12 +236,13 @@ class NestedInterfaceViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
 # Device bays
 #
 
-class DeviceBayViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class DeviceBayViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin,
+                       GenericViewSet):
     queryset = DeviceBay.objects.select_related('installed_device')
     serializer_class = serializers.DeviceBaySerializer
 
 
-class NestedDeviceBayViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class NestedDeviceBayViewSet(CreateModelMixin, ListModelMixin, WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.ChildDeviceBaySerializer
 
     def get_queryset(self):
@@ -248,12 +254,12 @@ class NestedDeviceBayViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
 # Modules
 #
 
-class ModuleViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
+class ModuleViewSet(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, WritableSerializerMixin, GenericViewSet):
     queryset = Module.objects.select_related('device', 'manufacturer')
     serializer_class = serializers.ModuleSerializer
 
 
-class NestedModuleViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
+class NestedModuleViewSet(CreateModelMixin, ListModelMixin, WritableSerializerMixin, GenericViewSet):
     serializer_class = serializers.ChildModuleSerializer
 
     def get_queryset(self):

+ 2 - 1
netbox/ipam/api/serializers.py

@@ -4,6 +4,7 @@ from dcim.api.serializers import NestedDeviceSerializer, ChildInterfaceSerialize
 from extras.api.serializers import CustomFieldSerializer
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from tenancy.api.serializers import NestedTenantSerializer
+from utilities.api import WritableSerializerMixin
 
 
 #
@@ -84,7 +85,7 @@ class NestedAggregateSerializer(serializers.HyperlinkedModelSerializer):
 # VLAN groups
 #
 
-class VLANGroupSerializer(serializers.ModelSerializer):
+class VLANGroupSerializer(WritableSerializerMixin, serializers.ModelSerializer):
     site = NestedSiteSerializer()
 
     class Meta:

+ 8 - 7
netbox/ipam/api/views.py

@@ -4,6 +4,7 @@ from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN,
 from ipam import filters
 
 from extras.api.views import CustomFieldModelViewSet
+from utilities.api import WritableSerializerMixin
 from . import serializers
 
 
@@ -11,7 +12,7 @@ from . import serializers
 # VRFs
 #
 
-class VRFViewSet(CustomFieldModelViewSet):
+class VRFViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = VRF.objects.select_related('tenant')
     serializer_class = serializers.VRFSerializer
     filter_class = filters.VRFFilter
@@ -39,7 +40,7 @@ class RIRViewSet(ModelViewSet):
 # Aggregates
 #
 
-class AggregateViewSet(CustomFieldModelViewSet):
+class AggregateViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Aggregate.objects.select_related('rir')
     serializer_class = serializers.AggregateSerializer
     filter_class = filters.AggregateFilter
@@ -49,7 +50,7 @@ class AggregateViewSet(CustomFieldModelViewSet):
 # Prefixes
 #
 
-class PrefixViewSet(CustomFieldModelViewSet):
+class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
     serializer_class = serializers.PrefixSerializer
     filter_class = filters.PrefixFilter
@@ -59,7 +60,7 @@ class PrefixViewSet(CustomFieldModelViewSet):
 # IP addresses
 #
 
-class IPAddressViewSet(CustomFieldModelViewSet):
+class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')
     serializer_class = serializers.IPAddressSerializer
     filter_class = filters.IPAddressFilter
@@ -69,7 +70,7 @@ class IPAddressViewSet(CustomFieldModelViewSet):
 # VLAN groups
 #
 
-class VLANGroupViewSet(ModelViewSet):
+class VLANGroupViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = VLANGroup.objects.select_related('site')
     serializer_class = serializers.VLANGroupSerializer
     filter_class = filters.VLANGroupFilter
@@ -79,7 +80,7 @@ class VLANGroupViewSet(ModelViewSet):
 # VLANs
 #
 
-class VLANViewSet(CustomFieldModelViewSet):
+class VLANViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
     serializer_class = serializers.VLANSerializer
     filter_class = filters.VLANFilter
@@ -89,7 +90,7 @@ class VLANViewSet(CustomFieldModelViewSet):
 # Services
 #
 
-class ServiceViewSet(ModelViewSet):
+class ServiceViewSet(WritableSerializerMixin, ModelViewSet):
     queryset = Service.objects.select_related('device').prefetch_related('ipaddresses')
     serializer_class = serializers.ServiceSerializer
     filter_class = filters.ServiceFilter

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

@@ -4,6 +4,7 @@ from tenancy.models import Tenant, TenantGroup
 from tenancy.filters import TenantFilter
 
 from extras.api.views import CustomFieldModelViewSet
+from utilities.api import WritableSerializerMixin
 from . import serializers
 
 
@@ -20,7 +21,7 @@ class TenantGroupViewSet(ModelViewSet):
 # Tenants
 #
 
-class TenantViewSet(CustomFieldModelViewSet):
+class TenantViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
     queryset = Tenant.objects.select_related('group')
     serializer_class = serializers.TenantSerializer
     filter_class = TenantFilter

+ 24 - 0
netbox/utilities/api.py

@@ -1,6 +1,30 @@
 from rest_framework.exceptions import APIException
+from rest_framework.serializers import ModelSerializer
+
+
+WRITE_OPERATIONS = ['create', 'update', 'partial_update', 'delete']
 
 
 class ServiceUnavailable(APIException):
     status_code = 503
     default_detail = "Service temporarily unavailable, please try again later."
+
+
+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.
+    """
+
+    def get_serializer_class(self):
+
+        class WritableSerializer(ModelSerializer):
+
+            class Meta:
+                model = self.queryset.model
+                fields = '__all__'
+
+        if self.action in WRITE_OPERATIONS:
+            return WritableSerializer
+
+        return self.serializer_class