Browse Source

Closes #1553: Introduced support for bulk object creation via the API

Jeremy Stretch 7 years ago
parent
commit
198170ca48

+ 4 - 5
netbox/circuits/api/views.py

@@ -3,14 +3,13 @@ from __future__ import unicode_literals
 from django.shortcuts import get_object_or_404
 from rest_framework.decorators import detail_route
 from rest_framework.response import Response
-from rest_framework.viewsets import ModelViewSet
 
 from circuits import filters
 from circuits.models import Provider, CircuitTermination, CircuitType, Circuit
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.models import Graph, GRAPH_TYPE_PROVIDER
-from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, ModelViewSet
 from . import serializers
 
 
@@ -28,7 +27,7 @@ class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
 # Providers
 #
 
-class ProviderViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class ProviderViewSet(CustomFieldModelViewSet):
     queryset = Provider.objects.all()
     serializer_class = serializers.ProviderSerializer
     write_serializer_class = serializers.WritableProviderSerializer
@@ -59,7 +58,7 @@ class CircuitTypeViewSet(ModelViewSet):
 # Circuits
 #
 
-class CircuitViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class CircuitViewSet(CustomFieldModelViewSet):
     queryset = Circuit.objects.select_related('type', 'tenant', 'provider')
     serializer_class = serializers.CircuitSerializer
     write_serializer_class = serializers.WritableCircuitSerializer
@@ -70,7 +69,7 @@ class CircuitViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Circuit Terminations
 #
 
-class CircuitTerminationViewSet(WritableSerializerMixin, ModelViewSet):
+class CircuitTerminationViewSet(ModelViewSet):
     queryset = CircuitTermination.objects.select_related('circuit', 'site', 'interface__device')
     serializer_class = serializers.CircuitTerminationSerializer
     write_serializer_class = serializers.WritableCircuitTerminationSerializer

+ 23 - 25
netbox/dcim/api/views.py

@@ -8,7 +8,7 @@ from django.shortcuts import get_object_or_404
 from rest_framework.decorators import detail_route
 from rest_framework.mixins import ListModelMixin
 from rest_framework.response import Response
-from rest_framework.viewsets import GenericViewSet, ModelViewSet, ViewSet
+from rest_framework.viewsets import GenericViewSet, ViewSet
 
 from dcim import filters
 from dcim.models import (
@@ -20,9 +20,7 @@ from dcim.models import (
 from extras.api.serializers import RenderedGraphSerializer
 from extras.api.views import CustomFieldModelViewSet
 from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
-from utilities.api import (
-    IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ServiceUnavailable, WritableSerializerMixin,
-)
+from utilities.api import IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable
 from . import serializers
 from .exceptions import MissingFilterException
 
@@ -47,7 +45,7 @@ class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
 # Regions
 #
 
-class RegionViewSet(WritableSerializerMixin, ModelViewSet):
+class RegionViewSet(ModelViewSet):
     queryset = Region.objects.all()
     serializer_class = serializers.RegionSerializer
     write_serializer_class = serializers.WritableRegionSerializer
@@ -58,7 +56,7 @@ class RegionViewSet(WritableSerializerMixin, ModelViewSet):
 # Sites
 #
 
-class SiteViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class SiteViewSet(CustomFieldModelViewSet):
     queryset = Site.objects.select_related('region', 'tenant')
     serializer_class = serializers.SiteSerializer
     write_serializer_class = serializers.WritableSiteSerializer
@@ -79,7 +77,7 @@ class SiteViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Rack groups
 #
 
-class RackGroupViewSet(WritableSerializerMixin, ModelViewSet):
+class RackGroupViewSet(ModelViewSet):
     queryset = RackGroup.objects.select_related('site')
     serializer_class = serializers.RackGroupSerializer
     write_serializer_class = serializers.WritableRackGroupSerializer
@@ -100,7 +98,7 @@ class RackRoleViewSet(ModelViewSet):
 # Racks
 #
 
-class RackViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class RackViewSet(CustomFieldModelViewSet):
     queryset = Rack.objects.select_related('site', 'group__site', 'tenant')
     serializer_class = serializers.RackSerializer
     write_serializer_class = serializers.WritableRackSerializer
@@ -131,7 +129,7 @@ class RackViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Rack reservations
 #
 
-class RackReservationViewSet(WritableSerializerMixin, ModelViewSet):
+class RackReservationViewSet(ModelViewSet):
     queryset = RackReservation.objects.select_related('rack')
     serializer_class = serializers.RackReservationSerializer
     write_serializer_class = serializers.WritableRackReservationSerializer
@@ -156,7 +154,7 @@ class ManufacturerViewSet(ModelViewSet):
 # Device types
 #
 
-class DeviceTypeViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class DeviceTypeViewSet(CustomFieldModelViewSet):
     queryset = DeviceType.objects.select_related('manufacturer')
     serializer_class = serializers.DeviceTypeSerializer
     write_serializer_class = serializers.WritableDeviceTypeSerializer
@@ -167,42 +165,42 @@ class DeviceTypeViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Device type components
 #
 
-class ConsolePortTemplateViewSet(WritableSerializerMixin, ModelViewSet):
+class ConsolePortTemplateViewSet(ModelViewSet):
     queryset = ConsolePortTemplate.objects.select_related('device_type__manufacturer')
     serializer_class = serializers.ConsolePortTemplateSerializer
     write_serializer_class = serializers.WritableConsolePortTemplateSerializer
     filter_class = filters.ConsolePortTemplateFilter
 
 
-class ConsoleServerPortTemplateViewSet(WritableSerializerMixin, ModelViewSet):
+class ConsoleServerPortTemplateViewSet(ModelViewSet):
     queryset = ConsoleServerPortTemplate.objects.select_related('device_type__manufacturer')
     serializer_class = serializers.ConsoleServerPortTemplateSerializer
     write_serializer_class = serializers.WritableConsoleServerPortTemplateSerializer
     filter_class = filters.ConsoleServerPortTemplateFilter
 
 
-class PowerPortTemplateViewSet(WritableSerializerMixin, ModelViewSet):
+class PowerPortTemplateViewSet(ModelViewSet):
     queryset = PowerPortTemplate.objects.select_related('device_type__manufacturer')
     serializer_class = serializers.PowerPortTemplateSerializer
     write_serializer_class = serializers.WritablePowerPortTemplateSerializer
     filter_class = filters.PowerPortTemplateFilter
 
 
-class PowerOutletTemplateViewSet(WritableSerializerMixin, ModelViewSet):
+class PowerOutletTemplateViewSet(ModelViewSet):
     queryset = PowerOutletTemplate.objects.select_related('device_type__manufacturer')
     serializer_class = serializers.PowerOutletTemplateSerializer
     write_serializer_class = serializers.WritablePowerOutletTemplateSerializer
     filter_class = filters.PowerOutletTemplateFilter
 
 
-class InterfaceTemplateViewSet(WritableSerializerMixin, ModelViewSet):
+class InterfaceTemplateViewSet(ModelViewSet):
     queryset = InterfaceTemplate.objects.select_related('device_type__manufacturer')
     serializer_class = serializers.InterfaceTemplateSerializer
     write_serializer_class = serializers.WritableInterfaceTemplateSerializer
     filter_class = filters.InterfaceTemplateFilter
 
 
-class DeviceBayTemplateViewSet(WritableSerializerMixin, ModelViewSet):
+class DeviceBayTemplateViewSet(ModelViewSet):
     queryset = DeviceBayTemplate.objects.select_related('device_type__manufacturer')
     serializer_class = serializers.DeviceBayTemplateSerializer
     write_serializer_class = serializers.WritableDeviceBayTemplateSerializer
@@ -233,7 +231,7 @@ class PlatformViewSet(ModelViewSet):
 # Devices
 #
 
-class DeviceViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class DeviceViewSet(CustomFieldModelViewSet):
     queryset = Device.objects.select_related(
         'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay',
     ).prefetch_related(
@@ -309,35 +307,35 @@ class DeviceViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Device components
 #
 
-class ConsolePortViewSet(WritableSerializerMixin, ModelViewSet):
+class ConsolePortViewSet(ModelViewSet):
     queryset = ConsolePort.objects.select_related('device', 'cs_port__device')
     serializer_class = serializers.ConsolePortSerializer
     write_serializer_class = serializers.WritableConsolePortSerializer
     filter_class = filters.ConsolePortFilter
 
 
-class ConsoleServerPortViewSet(WritableSerializerMixin, ModelViewSet):
+class ConsoleServerPortViewSet(ModelViewSet):
     queryset = ConsoleServerPort.objects.select_related('device', 'connected_console__device')
     serializer_class = serializers.ConsoleServerPortSerializer
     write_serializer_class = serializers.WritableConsoleServerPortSerializer
     filter_class = filters.ConsoleServerPortFilter
 
 
-class PowerPortViewSet(WritableSerializerMixin, ModelViewSet):
+class PowerPortViewSet(ModelViewSet):
     queryset = PowerPort.objects.select_related('device', 'power_outlet__device')
     serializer_class = serializers.PowerPortSerializer
     write_serializer_class = serializers.WritablePowerPortSerializer
     filter_class = filters.PowerPortFilter
 
 
-class PowerOutletViewSet(WritableSerializerMixin, ModelViewSet):
+class PowerOutletViewSet(ModelViewSet):
     queryset = PowerOutlet.objects.select_related('device', 'connected_port__device')
     serializer_class = serializers.PowerOutletSerializer
     write_serializer_class = serializers.WritablePowerOutletSerializer
     filter_class = filters.PowerOutletFilter
 
 
-class InterfaceViewSet(WritableSerializerMixin, ModelViewSet):
+class InterfaceViewSet(ModelViewSet):
     queryset = Interface.objects.select_related('device')
     serializer_class = serializers.InterfaceSerializer
     write_serializer_class = serializers.WritableInterfaceSerializer
@@ -354,14 +352,14 @@ class InterfaceViewSet(WritableSerializerMixin, ModelViewSet):
         return Response(serializer.data)
 
 
-class DeviceBayViewSet(WritableSerializerMixin, ModelViewSet):
+class DeviceBayViewSet(ModelViewSet):
     queryset = DeviceBay.objects.select_related('installed_device')
     serializer_class = serializers.DeviceBaySerializer
     write_serializer_class = serializers.WritableDeviceBaySerializer
     filter_class = filters.DeviceBayFilter
 
 
-class InventoryItemViewSet(WritableSerializerMixin, ModelViewSet):
+class InventoryItemViewSet(ModelViewSet):
     queryset = InventoryItem.objects.select_related('device', 'manufacturer')
     serializer_class = serializers.InventoryItemSerializer
     write_serializer_class = serializers.WritableInventoryItemSerializer
@@ -384,7 +382,7 @@ class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
     filter_class = filters.PowerConnectionFilter
 
 
-class InterfaceConnectionViewSet(WritableSerializerMixin, ModelViewSet):
+class InterfaceConnectionViewSet(ModelViewSet):
     queryset = InterfaceConnection.objects.select_related('interface_a__device', 'interface_b__device')
     serializer_class = serializers.InterfaceConnectionSerializer
     write_serializer_class = serializers.WritableInterfaceConnectionSerializer

+ 6 - 6
netbox/extras/api/views.py

@@ -6,12 +6,12 @@ from django.shortcuts import get_object_or_404
 from rest_framework.decorators import detail_route
 from rest_framework.exceptions import PermissionDenied
 from rest_framework.response import Response
-from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet
+from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
 
 from extras import filters
 from extras.models import CustomField, ExportTemplate, Graph, ImageAttachment, ReportResult, TopologyMap, UserAction
 from extras.reports import get_report, get_reports
-from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
 from . import serializers
 
 
@@ -64,7 +64,7 @@ class CustomFieldModelViewSet(ModelViewSet):
 # Graphs
 #
 
-class GraphViewSet(WritableSerializerMixin, ModelViewSet):
+class GraphViewSet(ModelViewSet):
     queryset = Graph.objects.all()
     serializer_class = serializers.GraphSerializer
     write_serializer_class = serializers.WritableGraphSerializer
@@ -75,7 +75,7 @@ class GraphViewSet(WritableSerializerMixin, ModelViewSet):
 # Export templates
 #
 
-class ExportTemplateViewSet(WritableSerializerMixin, ModelViewSet):
+class ExportTemplateViewSet(ModelViewSet):
     queryset = ExportTemplate.objects.all()
     serializer_class = serializers.ExportTemplateSerializer
     filter_class = filters.ExportTemplateFilter
@@ -85,7 +85,7 @@ class ExportTemplateViewSet(WritableSerializerMixin, ModelViewSet):
 # Topology maps
 #
 
-class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
+class TopologyMapViewSet(ModelViewSet):
     queryset = TopologyMap.objects.select_related('site')
     serializer_class = serializers.TopologyMapSerializer
     write_serializer_class = serializers.WritableTopologyMapSerializer
@@ -115,7 +115,7 @@ class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
 # Image attachments
 #
 
-class ImageAttachmentViewSet(WritableSerializerMixin, ModelViewSet):
+class ImageAttachmentViewSet(ModelViewSet):
     queryset = ImageAttachment.objects.all()
     serializer_class = serializers.ImageAttachmentSerializer
     write_serializer_class = serializers.WritableImageAttachmentSerializer

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

@@ -6,12 +6,11 @@ from rest_framework import status
 from rest_framework.decorators import detail_route
 from rest_framework.exceptions import PermissionDenied
 from rest_framework.response import Response
-from rest_framework.viewsets import ModelViewSet
 
 from extras.api.views import CustomFieldModelViewSet
 from ipam import filters
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
-from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, ModelViewSet
 from . import serializers
 
 
@@ -33,7 +32,7 @@ class IPAMFieldChoicesViewSet(FieldChoicesViewSet):
 # VRFs
 #
 
-class VRFViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class VRFViewSet(CustomFieldModelViewSet):
     queryset = VRF.objects.select_related('tenant')
     serializer_class = serializers.VRFSerializer
     write_serializer_class = serializers.WritableVRFSerializer
@@ -54,7 +53,7 @@ class RIRViewSet(ModelViewSet):
 # Aggregates
 #
 
-class AggregateViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class AggregateViewSet(CustomFieldModelViewSet):
     queryset = Aggregate.objects.select_related('rir')
     serializer_class = serializers.AggregateSerializer
     write_serializer_class = serializers.WritableAggregateSerializer
@@ -75,7 +74,7 @@ class RoleViewSet(ModelViewSet):
 # Prefixes
 #
 
-class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class PrefixViewSet(CustomFieldModelViewSet):
     queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
     serializer_class = serializers.PrefixSerializer
     write_serializer_class = serializers.WritablePrefixSerializer
@@ -146,7 +145,7 @@ class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # IP addresses
 #
 
-class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class IPAddressViewSet(CustomFieldModelViewSet):
     queryset = IPAddress.objects.select_related(
         'vrf__tenant', 'tenant', 'nat_inside'
     ).prefetch_related(
@@ -161,7 +160,7 @@ class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # VLAN groups
 #
 
-class VLANGroupViewSet(WritableSerializerMixin, ModelViewSet):
+class VLANGroupViewSet(ModelViewSet):
     queryset = VLANGroup.objects.select_related('site')
     serializer_class = serializers.VLANGroupSerializer
     write_serializer_class = serializers.WritableVLANGroupSerializer
@@ -172,7 +171,7 @@ class VLANGroupViewSet(WritableSerializerMixin, ModelViewSet):
 # VLANs
 #
 
-class VLANViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class VLANViewSet(CustomFieldModelViewSet):
     queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
     serializer_class = serializers.VLANSerializer
     write_serializer_class = serializers.WritableVLANSerializer
@@ -183,7 +182,7 @@ class VLANViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Services
 #
 
-class ServiceViewSet(WritableSerializerMixin, ModelViewSet):
+class ServiceViewSet(ModelViewSet):
     queryset = Service.objects.select_related('device')
     serializer_class = serializers.ServiceSerializer
     write_serializer_class = serializers.WritableServiceSerializer

+ 3 - 3
netbox/secrets/api/views.py

@@ -7,12 +7,12 @@ from django.http import HttpResponseBadRequest
 from rest_framework.exceptions import ValidationError
 from rest_framework.permissions import IsAuthenticated
 from rest_framework.response import Response
-from rest_framework.viewsets import ModelViewSet, ViewSet
+from rest_framework.viewsets import ViewSet
 
 from secrets import filters
 from secrets.exceptions import InvalidKey
 from secrets.models import Secret, SecretRole, SessionKey, UserKey
-from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, ModelViewSet
 from . import serializers
 
 ERR_USERKEY_MISSING = "No UserKey found for the current user."
@@ -44,7 +44,7 @@ class SecretRoleViewSet(ModelViewSet):
 # Secrets
 #
 
-class SecretViewSet(WritableSerializerMixin, ModelViewSet):
+class SecretViewSet(ModelViewSet):
     queryset = Secret.objects.select_related(
         'device__primary_ip4', 'device__primary_ip6', 'role',
     ).prefetch_related(

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

@@ -1,11 +1,9 @@
 from __future__ import unicode_literals
 
-from rest_framework.viewsets import ModelViewSet
-
 from extras.api.views import CustomFieldModelViewSet
 from tenancy import filters
 from tenancy.models import Tenant, TenantGroup
-from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, ModelViewSet
 from . import serializers
 
 
@@ -31,7 +29,7 @@ class TenantGroupViewSet(ModelViewSet):
 # Tenants
 #
 
-class TenantViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class TenantViewSet(CustomFieldModelViewSet):
     queryset = Tenant.objects.select_related('group')
     serializer_class = serializers.TenantSerializer
     write_serializer_class = serializers.WritableTenantSerializer

+ 27 - 18
netbox/utilities/api.py

@@ -5,11 +5,12 @@ from collections import OrderedDict
 from django.conf import settings
 from django.contrib.contenttypes.models import ContentType
 from django.http import Http404
+from rest_framework import mixins
 from rest_framework.exceptions import APIException
 from rest_framework.permissions import BasePermission
 from rest_framework.response import Response
 from rest_framework.serializers import Field, ModelSerializer, ValidationError
-from rest_framework.viewsets import ViewSet
+from rest_framework.viewsets import GenericViewSet, ViewSet
 
 WRITE_OPERATIONS = ['create', 'update', 'partial_update', 'delete']
 
@@ -97,9 +98,33 @@ class ContentTypeFieldSerializer(Field):
 
 
 #
-# Views
+# Viewsets
 #
 
+class ModelViewSet(mixins.CreateModelMixin,
+                   mixins.RetrieveModelMixin,
+                   mixins.UpdateModelMixin,
+                   mixins.DestroyModelMixin,
+                   mixins.ListModelMixin,
+                   GenericViewSet):
+    """
+    Substitute DRF's built-in ModelViewSet for our own, which introduces a bit of additional functionality:
+    1. Use an alternate serializer (if provided) for write operations
+    2. Accept either a single object or a list of objects to create
+    """
+    def get_serializer_class(self):
+        # Check for a different serializer to use for write operations
+        if self.action in WRITE_OPERATIONS and hasattr(self, 'write_serializer_class'):
+            return self.write_serializer_class
+        return self.serializer_class
+
+    def get_serializer(self, *args, **kwargs):
+        # If a list of objects has been provided, initialize the serializer with many=True
+        if isinstance(kwargs.get('data', {}), list):
+            kwargs['many'] = True
+        return super(ModelViewSet, self).get_serializer(*args, **kwargs)
+
+
 class FieldChoicesViewSet(ViewSet):
     """
     Expose the built-in numeric values which represent static choices for a model's field.
@@ -135,25 +160,9 @@ class FieldChoicesViewSet(ViewSet):
         return Response(self._fields)
 
     def retrieve(self, request, pk):
-
         if pk not in self._fields:
             raise Http404
-
         return Response(self._fields[pk])
 
     def get_view_name(self):
         return "Field Choices"
-
-
-#
-# Mixins
-#
-
-class WritableSerializerMixin(object):
-    """
-    Allow for the use of an alternate, writable serializer class for write operations (e.g. POST, PUT).
-    """
-    def get_serializer_class(self):
-        if self.action in WRITE_OPERATIONS and hasattr(self, 'write_serializer_class'):
-            return self.write_serializer_class
-        return self.serializer_class

+ 4 - 6
netbox/virtualization/api/views.py

@@ -1,10 +1,8 @@
 from __future__ import unicode_literals
 
-from rest_framework.viewsets import ModelViewSet
-
 from dcim.models import Interface
 from extras.api.views import CustomFieldModelViewSet
-from utilities.api import FieldChoicesViewSet, WritableSerializerMixin
+from utilities.api import FieldChoicesViewSet, ModelViewSet
 from virtualization import filters
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from . import serializers
@@ -34,7 +32,7 @@ class ClusterGroupViewSet(ModelViewSet):
     serializer_class = serializers.ClusterGroupSerializer
 
 
-class ClusterViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class ClusterViewSet(CustomFieldModelViewSet):
     queryset = Cluster.objects.select_related('type', 'group')
     serializer_class = serializers.ClusterSerializer
     write_serializer_class = serializers.WritableClusterSerializer
@@ -45,14 +43,14 @@ class ClusterViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Virtual machines
 #
 
-class VirtualMachineViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
+class VirtualMachineViewSet(CustomFieldModelViewSet):
     queryset = VirtualMachine.objects.all()
     serializer_class = serializers.VirtualMachineSerializer
     write_serializer_class = serializers.WritableVirtualMachineSerializer
     filter_class = filters.VirtualMachineFilter
 
 
-class InterfaceViewSet(WritableSerializerMixin, ModelViewSet):
+class InterfaceViewSet(ModelViewSet):
     queryset = Interface.objects.filter(virtual_machine__isnull=False).select_related('virtual_machine')
     serializer_class = serializers.InterfaceSerializer
     write_serializer_class = serializers.WritableInterfaceSerializer