Skip to content

Commit

Permalink
Fixes netbox-community#1285: Enforce model validation when creating/e…
Browse files Browse the repository at this point in the history
…diting objects via the API
  • Loading branch information
jeremystretch committed Jul 6, 2017
1 parent be89a78 commit 72f24f5
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 31 deletions.
5 changes: 3 additions & 2 deletions netbox/circuits/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
from extras.api.customfields import CustomFieldModelSerializer
from tenancy.api.serializers import NestedTenantSerializer
from utilities.api import ModelValidationMixin


#
Expand Down Expand Up @@ -44,7 +45,7 @@ class Meta:
# Circuit types
#

class CircuitTypeSerializer(serializers.ModelSerializer):
class CircuitTypeSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = CircuitType
Expand Down Expand Up @@ -110,7 +111,7 @@ class Meta:
]


class WritableCircuitTerminationSerializer(serializers.ModelSerializer):
class WritableCircuitTerminationSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = CircuitTermination
Expand Down
50 changes: 28 additions & 22 deletions netbox/dcim/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)
from extras.api.customfields import CustomFieldModelSerializer
from tenancy.api.serializers import NestedTenantSerializer
from utilities.api import ChoiceFieldSerializer
from utilities.api import ChoiceFieldSerializer, ModelValidationMixin


#
Expand All @@ -36,7 +36,7 @@ class Meta:
fields = ['id', 'name', 'slug', 'parent']


class WritableRegionSerializer(serializers.ModelSerializer):
class WritableRegionSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = Region
Expand Down Expand Up @@ -98,7 +98,7 @@ class Meta:
fields = ['id', 'url', 'name', 'slug']


class WritableRackGroupSerializer(serializers.ModelSerializer):
class WritableRackGroupSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = RackGroup
Expand All @@ -109,7 +109,7 @@ class Meta:
# Rack roles
#

class RackRoleSerializer(serializers.ModelSerializer):
class RackRoleSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = RackRole
Expand Down Expand Up @@ -174,6 +174,9 @@ def validate(self, data):
validator.set_context(self)
validator(data)

# Enforce model validation
super(WritableRackSerializer, self).validate(data)

return data


Expand Down Expand Up @@ -211,7 +214,7 @@ class Meta:
fields = ['id', 'rack', 'units', 'created', 'user', 'description']


class WritableRackReservationSerializer(serializers.ModelSerializer):
class WritableRackReservationSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = RackReservation
Expand All @@ -222,7 +225,7 @@ class Meta:
# Manufacturers
#

class ManufacturerSerializer(serializers.ModelSerializer):
class ManufacturerSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = Manufacturer
Expand Down Expand Up @@ -287,7 +290,7 @@ class Meta:
fields = ['id', 'device_type', 'name']


class WritableConsolePortTemplateSerializer(serializers.ModelSerializer):
class WritableConsolePortTemplateSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = ConsolePortTemplate
Expand All @@ -306,7 +309,7 @@ class Meta:
fields = ['id', 'device_type', 'name']


class WritableConsoleServerPortTemplateSerializer(serializers.ModelSerializer):
class WritableConsoleServerPortTemplateSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = ConsoleServerPortTemplate
Expand All @@ -325,7 +328,7 @@ class Meta:
fields = ['id', 'device_type', 'name']


class WritablePowerPortTemplateSerializer(serializers.ModelSerializer):
class WritablePowerPortTemplateSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = PowerPortTemplate
Expand All @@ -344,7 +347,7 @@ class Meta:
fields = ['id', 'device_type', 'name']


class WritablePowerOutletTemplateSerializer(serializers.ModelSerializer):
class WritablePowerOutletTemplateSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = PowerOutletTemplate
Expand All @@ -364,7 +367,7 @@ class Meta:
fields = ['id', 'device_type', 'name', 'form_factor', 'mgmt_only']


class WritableInterfaceTemplateSerializer(serializers.ModelSerializer):
class WritableInterfaceTemplateSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = InterfaceTemplate
Expand All @@ -383,7 +386,7 @@ class Meta:
fields = ['id', 'device_type', 'name']


class WritableDeviceBayTemplateSerializer(serializers.ModelSerializer):
class WritableDeviceBayTemplateSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = DeviceBayTemplate
Expand All @@ -394,7 +397,7 @@ class Meta:
# Device roles
#

class DeviceRoleSerializer(serializers.ModelSerializer):
class DeviceRoleSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = DeviceRole
Expand All @@ -413,7 +416,7 @@ class Meta:
# Platforms
#

class PlatformSerializer(serializers.ModelSerializer):
class PlatformSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = Platform
Expand Down Expand Up @@ -496,6 +499,9 @@ def validate(self, data):
validator.set_context(self)
validator(data)

# Enforce model validation
super(WritableDeviceSerializer, self).validate(data)

return data


Expand All @@ -512,7 +518,7 @@ class Meta:
read_only_fields = ['connected_console']


class WritableConsoleServerPortSerializer(serializers.ModelSerializer):
class WritableConsoleServerPortSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = ConsoleServerPort
Expand All @@ -532,7 +538,7 @@ class Meta:
fields = ['id', 'device', 'name', 'cs_port', 'connection_status']


class WritableConsolePortSerializer(serializers.ModelSerializer):
class WritableConsolePortSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = ConsolePort
Expand All @@ -552,7 +558,7 @@ class Meta:
read_only_fields = ['connected_port']


class WritablePowerOutletSerializer(serializers.ModelSerializer):
class WritablePowerOutletSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = PowerOutlet
Expand All @@ -572,7 +578,7 @@ class Meta:
fields = ['id', 'device', 'name', 'power_outlet', 'connection_status']


class WritablePowerPortSerializer(serializers.ModelSerializer):
class WritablePowerPortSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = PowerPort
Expand Down Expand Up @@ -630,7 +636,7 @@ class Meta:
]


class WritableInterfaceSerializer(serializers.ModelSerializer):
class WritableInterfaceSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = Interface
Expand All @@ -652,7 +658,7 @@ class Meta:
fields = ['id', 'device', 'name', 'installed_device']


class WritableDeviceBaySerializer(serializers.ModelSerializer):
class WritableDeviceBaySerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = DeviceBay
Expand All @@ -675,7 +681,7 @@ class Meta:
]


class WritableInventoryItemSerializer(serializers.ModelSerializer):
class WritableInventoryItemSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = InventoryItem
Expand Down Expand Up @@ -707,7 +713,7 @@ class Meta:
fields = ['id', 'url', 'connection_status']


class WritableInterfaceConnectionSerializer(serializers.ModelSerializer):
class WritableInterfaceConnectionSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = InterfaceConnection
Expand Down
10 changes: 10 additions & 0 deletions netbox/extras/api/customfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ def _save_custom_fields(self, instance, custom_fields):
defaults={'serialized_value': custom_field.serialize_value(value)},
)

def validate(self, data):
"""
Enforce model validation (see utilities.api.ModelValidationMixin)
"""
model_data = data.copy()
model_data.pop('custom_fields', None)
instance = self.Meta.model(**model_data)
instance.clean()
return data

def create(self, validated_data):

custom_fields = validated_data.pop('custom_fields', None)
Expand Down
7 changes: 5 additions & 2 deletions netbox/extras/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
ACTION_CHOICES, ExportTemplate, Graph, GRAPH_TYPE_CHOICES, ImageAttachment, TopologyMap, UserAction,
)
from users.api.serializers import NestedUserSerializer
from utilities.api import ChoiceFieldSerializer, ContentTypeFieldSerializer
from utilities.api import ChoiceFieldSerializer, ContentTypeFieldSerializer, ModelValidationMixin


#
Expand Down Expand Up @@ -104,7 +104,7 @@ def get_parent(self, obj):
return serializer(obj.parent, context={'request': self.context['request']}).data


class WritableImageAttachmentSerializer(serializers.ModelSerializer):
class WritableImageAttachmentSerializer(ModelValidationMixin, serializers.ModelSerializer):
content_type = ContentTypeFieldSerializer()

class Meta:
Expand All @@ -121,6 +121,9 @@ def validate(self, data):
"Invalid parent object: {} ID {}".format(data['content_type'], data['object_id'])
)

# Enforce model validation
super(WritableImageAttachmentSerializer, self).validate(data)

return data


Expand Down
13 changes: 10 additions & 3 deletions netbox/ipam/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
PREFIX_STATUS_CHOICES, RIR, Role, Service, VLAN, VLAN_STATUS_CHOICES, VLANGroup, VRF,
)
from tenancy.api.serializers import NestedTenantSerializer
from utilities.api import ChoiceFieldSerializer
from utilities.api import ChoiceFieldSerializer, ModelValidationMixin


#
Expand Down Expand Up @@ -45,7 +45,7 @@ class Meta:
# Roles
#

class RoleSerializer(serializers.ModelSerializer):
class RoleSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = Role
Expand All @@ -64,7 +64,7 @@ class Meta:
# RIRs
#

class RIRSerializer(serializers.ModelSerializer):
class RIRSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = RIR
Expand Down Expand Up @@ -142,6 +142,9 @@ def validate(self, data):
validator.set_context(self)
validator(data)

# Enforce model validation
super(WritableVLANGroupSerializer, self).validate(data)

return data


Expand Down Expand Up @@ -188,6 +191,9 @@ def validate(self, data):
validator.set_context(self)
validator(data)

# Enforce model validation
super(WritableVLANSerializer, self).validate(data)

return data


Expand Down Expand Up @@ -297,6 +303,7 @@ class Meta:
fields = ['id', 'device', 'name', 'port', 'protocol', 'ipaddresses', 'description']


# TODO: Figure out how to use ModelValidationMixin with ManyToManyFields. Calling clean() yields a ValueError.
class WritableServiceSerializer(serializers.ModelSerializer):

class Meta:
Expand Down
6 changes: 5 additions & 1 deletion netbox/secrets/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

from dcim.api.serializers import NestedDeviceSerializer
from secrets.models import Secret, SecretRole
from utilities.api import ModelValidationMixin


#
# SecretRoles
#

class SecretRoleSerializer(serializers.ModelSerializer):
class SecretRoleSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = SecretRole
Expand Down Expand Up @@ -55,4 +56,7 @@ def validate(self, data):
validator.set_context(self)
validator(data)

# Enforce model validation
super(WritableSecretSerializer, self).validate(data)

return data
3 changes: 2 additions & 1 deletion netbox/tenancy/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

from extras.api.customfields import CustomFieldModelSerializer
from tenancy.models import Tenant, TenantGroup
from utilities.api import ModelValidationMixin


#
# Tenant groups
#

class TenantGroupSerializer(serializers.ModelSerializer):
class TenantGroupSerializer(ModelValidationMixin, serializers.ModelSerializer):

class Meta:
model = TenantGroup
Expand Down
Loading

0 comments on commit 72f24f5

Please sign in to comment.