diff --git a/netbox/circuits/api/serializers.py b/netbox/circuits/api/serializers.py index 74d8136e0..5688bf1c7 100644 --- a/netbox/circuits/api/serializers.py +++ b/netbox/circuits/api/serializers.py @@ -4,7 +4,7 @@ from circuits.choices import CircuitStatusChoices from circuits.models import Provider, Circuit, CircuitTermination, CircuitType from dcim.api.nested_serializers import NestedCableSerializer, NestedSiteSerializer from dcim.api.serializers import CableTerminationSerializer, ConnectedEndpointSerializer -from extras.api.customfields import CustomFieldModelSerializer +from netbox.api.serializers import CustomFieldModelSerializer from extras.api.serializers import TaggedObjectSerializer from netbox.api import ChoiceField from netbox.api.serializers import OrganizationalModelSerializer, WritableNestedSerializer diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 7d86d18a1..b4ac77e2d 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -13,12 +13,14 @@ from dcim.models import ( PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis, ) -from extras.api.customfields import CustomFieldModelSerializer +from netbox.api.serializers import CustomFieldModelSerializer from extras.api.serializers import TaggedObjectSerializer from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer from ipam.models import VLAN from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField, TimeZoneField -from netbox.api.serializers import OrganizationalModelSerializer, ValidatedModelSerializer, WritableNestedSerializer +from netbox.api.serializers import ( + NestedGroupModelSerializer, OrganizationalModelSerializer, ValidatedModelSerializer, WritableNestedSerializer, +) from tenancy.api.nested_serializers import NestedTenantSerializer from users.api.nested_serializers import NestedUserSerializer from utilities.api import get_serializer_for_model @@ -79,11 +81,10 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer): # Regions/sites # -class RegionSerializer(CustomFieldModelSerializer): +class RegionSerializer(NestedGroupModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail') parent = NestedRegionSerializer(required=False, allow_null=True) site_count = serializers.IntegerField(read_only=True) - _depth = serializers.IntegerField(source='level', read_only=True) class Meta: model = Region @@ -120,12 +121,11 @@ class SiteSerializer(TaggedObjectSerializer, CustomFieldModelSerializer): # Racks # -class RackGroupSerializer(CustomFieldModelSerializer): +class RackGroupSerializer(NestedGroupModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail') site = NestedSiteSerializer() parent = NestedRackGroupSerializer(required=False, allow_null=True) rack_count = serializers.IntegerField(read_only=True) - _depth = serializers.IntegerField(source='level', read_only=True) class Meta: model = RackGroup diff --git a/netbox/extras/api/customfields.py b/netbox/extras/api/customfields.py index c8c4ba89e..5cb1fc276 100644 --- a/netbox/extras/api/customfields.py +++ b/netbox/extras/api/customfields.py @@ -1,9 +1,7 @@ from django.contrib.contenttypes.models import ContentType -from rest_framework.fields import CreateOnlyDefault, Field +from rest_framework.fields import Field -from extras.choices import * from extras.models import CustomField -from netbox.api import ValidatedModelSerializer # @@ -56,34 +54,3 @@ class CustomFieldsDataField(Field): data = {**self.parent.instance.custom_field_data, **data} return data - - -class CustomFieldModelSerializer(ValidatedModelSerializer): - """ - Extends ModelSerializer to render any CustomFields and their values associated with an object. - """ - custom_fields = CustomFieldsDataField( - source='custom_field_data', - default=CreateOnlyDefault(CustomFieldDefaultValues()) - ) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if self.instance is not None: - - # Retrieve the set of CustomFields which apply to this type of object - content_type = ContentType.objects.get_for_model(self.Meta.model) - fields = CustomField.objects.filter(content_types=content_type) - - # Populate CustomFieldValues for each instance from database - if type(self.instance) in (list, tuple): - for obj in self.instance: - self._populate_custom_fields(obj, fields) - else: - self._populate_custom_fields(self.instance, fields) - - def _populate_custom_fields(self, instance, custom_fields): - instance.custom_fields = {} - for field in custom_fields: - instance.custom_fields[field.name] = instance.cf.get(field.name) diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 8688da8f2..002ad3b89 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -6,7 +6,7 @@ from rest_framework import serializers from rest_framework.validators import UniqueTogetherValidator from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerializer -from extras.api.customfields import CustomFieldModelSerializer +from netbox.api.serializers import CustomFieldModelSerializer from extras.api.serializers import TaggedObjectSerializer from ipam.choices import * from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS diff --git a/netbox/netbox/api/serializers.py b/netbox/netbox/api/serializers.py index 02e382d4b..18fc112c8 100644 --- a/netbox/netbox/api/serializers.py +++ b/netbox/netbox/api/serializers.py @@ -1,8 +1,12 @@ +from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist from django.db.models import ManyToManyField from rest_framework import serializers from rest_framework.exceptions import ValidationError +from rest_framework.fields import CreateOnlyDefault +from extras.api.customfields import CustomFieldsDataField, CustomFieldDefaultValues +from extras.models import CustomField from utilities.utils import dict_to_filter_params @@ -35,10 +39,45 @@ class ValidatedModelSerializer(serializers.ModelSerializer): return data -class OrganizationalModelSerializer(ValidatedModelSerializer): +class CustomFieldModelSerializer(ValidatedModelSerializer): + """ + Extends ModelSerializer to render any CustomFields and their values associated with an object. + """ + custom_fields = CustomFieldsDataField( + source='custom_field_data', + default=CreateOnlyDefault(CustomFieldDefaultValues()) + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if self.instance is not None: + + # Retrieve the set of CustomFields which apply to this type of object + content_type = ContentType.objects.get_for_model(self.Meta.model) + fields = CustomField.objects.filter(content_types=content_type) + + # Populate CustomFieldValues for each instance from database + if type(self.instance) in (list, tuple): + for obj in self.instance: + self._populate_custom_fields(obj, fields) + else: + self._populate_custom_fields(self.instance, fields) + + def _populate_custom_fields(self, instance, custom_fields): + instance.custom_fields = {} + for field in custom_fields: + instance.custom_fields[field.name] = instance.cf.get(field.name) + + +class OrganizationalModelSerializer(CustomFieldModelSerializer): pass +class NestedGroupModelSerializer(CustomFieldModelSerializer): + _depth = serializers.IntegerField(source='level', read_only=True) + + class WritableNestedSerializer(serializers.ModelSerializer): """ Returns a nested representation of an object on read, but accepts only a primary key on write. diff --git a/netbox/secrets/api/serializers.py b/netbox/secrets/api/serializers.py index 627685de0..207836c4c 100644 --- a/netbox/secrets/api/serializers.py +++ b/netbox/secrets/api/serializers.py @@ -2,7 +2,7 @@ from django.contrib.contenttypes.models import ContentType from drf_yasg.utils import swagger_serializer_method from rest_framework import serializers -from extras.api.customfields import CustomFieldModelSerializer +from netbox.api.serializers import CustomFieldModelSerializer from extras.api.serializers import TaggedObjectSerializer from secrets.constants import SECRET_ASSIGNMENT_MODELS from secrets.models import Secret, SecretRole diff --git a/netbox/tenancy/api/serializers.py b/netbox/tenancy/api/serializers.py index d301ee3fe..c701e6b3b 100644 --- a/netbox/tenancy/api/serializers.py +++ b/netbox/tenancy/api/serializers.py @@ -1,8 +1,7 @@ from rest_framework import serializers -from extras.api.customfields import CustomFieldModelSerializer +from netbox.api.serializers import CustomFieldModelSerializer, NestedGroupModelSerializer from extras.api.serializers import TaggedObjectSerializer -from netbox.api import ValidatedModelSerializer from tenancy.models import Tenant, TenantGroup from .nested_serializers import * @@ -11,11 +10,10 @@ from .nested_serializers import * # Tenants # -class TenantGroupSerializer(CustomFieldModelSerializer): +class TenantGroupSerializer(NestedGroupModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail') parent = NestedTenantGroupSerializer(required=False, allow_null=True) tenant_count = serializers.IntegerField(read_only=True) - _depth = serializers.IntegerField(source='level', read_only=True) class Meta: model = TenantGroup diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index b8ccb99b0..e3b82e40a 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -3,7 +3,7 @@ from rest_framework import serializers from dcim.api.nested_serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer from dcim.choices import InterfaceModeChoices -from extras.api.customfields import CustomFieldModelSerializer +from netbox.api.serializers import CustomFieldModelSerializer from extras.api.serializers import TaggedObjectSerializer from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer from ipam.models import VLAN