mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Replace nested serializers with primary serializers where possible
This commit is contained in:
@ -2,13 +2,12 @@ from rest_framework import serializers
|
||||
|
||||
from circuits.choices import CircuitStatusChoices
|
||||
from circuits.models import *
|
||||
from dcim.api.nested_serializers import NestedSiteSerializer
|
||||
from dcim.api.serializers import CabledObjectSerializer
|
||||
from dcim.api.serializers import CabledObjectSerializer, SiteSerializer
|
||||
from ipam.api.nested_serializers import NestedASNSerializer
|
||||
from ipam.models import ASN
|
||||
from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
|
||||
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from tenancy.api.serializers import TenantSerializer
|
||||
from .nested_serializers import *
|
||||
|
||||
|
||||
@ -49,7 +48,7 @@ class ProviderSerializer(NetBoxModelSerializer):
|
||||
|
||||
class ProviderAccountSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
|
||||
provider = NestedProviderSerializer()
|
||||
provider = ProviderSerializer(nested=True)
|
||||
|
||||
class Meta:
|
||||
model = ProviderAccount
|
||||
@ -66,7 +65,7 @@ class ProviderAccountSerializer(NetBoxModelSerializer):
|
||||
|
||||
class ProviderNetworkSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
|
||||
provider = NestedProviderSerializer()
|
||||
provider = ProviderSerializer(nested=True)
|
||||
|
||||
class Meta:
|
||||
model = ProviderNetwork
|
||||
@ -98,8 +97,8 @@ class CircuitTypeSerializer(NetBoxModelSerializer):
|
||||
|
||||
class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
||||
site = NestedSiteSerializer(allow_null=True)
|
||||
provider_network = NestedProviderNetworkSerializer(allow_null=True)
|
||||
site = SiteSerializer(nested=True, allow_null=True)
|
||||
provider_network = ProviderNetworkSerializer(nested=True, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
@ -111,11 +110,11 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
|
||||
|
||||
class CircuitSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
||||
provider = NestedProviderSerializer()
|
||||
provider_account = NestedProviderAccountSerializer(required=False, allow_null=True)
|
||||
provider = ProviderSerializer(nested=True)
|
||||
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=CircuitStatusChoices, required=False)
|
||||
type = NestedCircuitTypeSerializer()
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
type = CircuitTypeSerializer(nested=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
|
||||
termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
|
||||
|
||||
@ -131,9 +130,9 @@ class CircuitSerializer(NetBoxModelSerializer):
|
||||
|
||||
class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
|
||||
circuit = NestedCircuitSerializer()
|
||||
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||
provider_network = NestedProviderNetworkSerializer(required=False, allow_null=True)
|
||||
circuit = CircuitSerializer(nested=True)
|
||||
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
||||
provider_network = ProviderNetworkSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
|
@ -4,7 +4,7 @@ from core.choices import JobStatusChoices
|
||||
from core.models import *
|
||||
from netbox.api.fields import ChoiceField
|
||||
from netbox.api.serializers import WritableNestedSerializer
|
||||
from users.api.nested_serializers import NestedUserSerializer
|
||||
from users.api.serializers import UserSerializer
|
||||
|
||||
__all__ = (
|
||||
'NestedDataFileSerializer',
|
||||
@ -32,7 +32,8 @@ class NestedDataFileSerializer(WritableNestedSerializer):
|
||||
class NestedJobSerializer(serializers.ModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
|
||||
status = ChoiceField(choices=JobStatusChoices)
|
||||
user = NestedUserSerializer(
|
||||
user = UserSerializer(
|
||||
nested=True,
|
||||
read_only=True
|
||||
)
|
||||
|
||||
|
@ -5,8 +5,7 @@ from core.models import *
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
|
||||
from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer
|
||||
from netbox.utils import get_data_backend_choices
|
||||
from users.api.nested_serializers import NestedUserSerializer
|
||||
from .nested_serializers import *
|
||||
from users.api.serializers import UserSerializer
|
||||
|
||||
__all__ = (
|
||||
'DataFileSerializer',
|
||||
@ -43,7 +42,8 @@ class DataFileSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='core-api:datafile-detail'
|
||||
)
|
||||
source = NestedDataSourceSerializer(
|
||||
source = DataSourceSerializer(
|
||||
nested=True,
|
||||
read_only=True
|
||||
)
|
||||
|
||||
@ -57,7 +57,8 @@ class DataFileSerializer(NetBoxModelSerializer):
|
||||
|
||||
class JobSerializer(BaseModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
|
||||
user = NestedUserSerializer(
|
||||
user = UserSerializer(
|
||||
nested=True,
|
||||
read_only=True
|
||||
)
|
||||
status = ChoiceField(choices=JobStatusChoices, read_only=True)
|
||||
|
@ -6,8 +6,6 @@ from netbox.api.fields import RelatedObjectCountField
|
||||
from netbox.api.serializers import WritableNestedSerializer
|
||||
|
||||
__all__ = [
|
||||
'ComponentNestedModuleSerializer',
|
||||
'ModuleBayNestedModuleSerializer',
|
||||
'NestedCableSerializer',
|
||||
'NestedConsolePortSerializer',
|
||||
'NestedConsolePortTemplateSerializer',
|
||||
@ -311,26 +309,6 @@ class ModuleNestedModuleBaySerializer(WritableNestedSerializer):
|
||||
fields = ['id', 'url', 'display', 'name']
|
||||
|
||||
|
||||
class ModuleBayNestedModuleSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
|
||||
|
||||
class Meta:
|
||||
model = models.Module
|
||||
fields = ['id', 'url', 'display', 'serial']
|
||||
|
||||
|
||||
class ComponentNestedModuleSerializer(WritableNestedSerializer):
|
||||
"""
|
||||
Used by device component serializers.
|
||||
"""
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
|
||||
module_bay = ModuleNestedModuleBaySerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.Module
|
||||
fields = ['id', 'url', 'display', 'device', 'module_bay']
|
||||
|
||||
|
||||
class NestedModuleSerializer(WritableNestedSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
|
||||
device = NestedDeviceSerializer(read_only=True)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@ from rest_framework.response import Response
|
||||
from rest_framework.status import HTTP_400_BAD_REQUEST
|
||||
|
||||
from netbox.api.renderers import TextRenderer
|
||||
from .nested_serializers import NestedConfigTemplateSerializer
|
||||
from .serializers import ConfigTemplateSerializer
|
||||
|
||||
__all__ = (
|
||||
'ConfigContextQuerySetMixin',
|
||||
@ -52,7 +52,7 @@ class ConfigTemplateRenderMixin:
|
||||
if request.accepted_renderer.format == 'txt':
|
||||
return Response(output)
|
||||
|
||||
template_serializer = NestedConfigTemplateSerializer(configtemplate, context={'request': request})
|
||||
template_serializer = ConfigTemplateSerializer(configtemplate, nested=True, context={'request': request})
|
||||
|
||||
return Response({
|
||||
'configtemplate': template_serializer.data,
|
||||
|
@ -5,8 +5,7 @@ from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.api.nested_serializers import NestedDataSourceSerializer, NestedDataFileSerializer, NestedJobSerializer
|
||||
from core.api.serializers import JobSerializer
|
||||
from core.api.serializers import DataFileSerializer, DataSourceSerializer, JobSerializer
|
||||
from core.models import ContentType
|
||||
from dcim.api.nested_serializers import (
|
||||
NestedDeviceRoleSerializer, NestedDeviceTypeSerializer, NestedLocationSerializer, NestedPlatformSerializer,
|
||||
@ -22,7 +21,7 @@ from netbox.api.serializers.features import TaggableModelSerializer
|
||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from users.api.nested_serializers import NestedUserSerializer
|
||||
from users.api.serializers import UserSerializer
|
||||
from utilities.api import get_serializer_for_model
|
||||
from virtualization.api.nested_serializers import (
|
||||
NestedClusterGroupSerializer, NestedClusterSerializer, NestedClusterTypeSerializer,
|
||||
@ -115,6 +114,28 @@ class WebhookSerializer(NetBoxModelSerializer):
|
||||
# Custom fields
|
||||
#
|
||||
|
||||
class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfieldchoiceset-detail')
|
||||
base_choices = ChoiceField(
|
||||
choices=CustomFieldChoiceSetBaseChoices,
|
||||
required=False
|
||||
)
|
||||
extra_choices = serializers.ListField(
|
||||
child=serializers.ListField(
|
||||
min_length=2,
|
||||
max_length=2
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CustomFieldChoiceSet
|
||||
fields = [
|
||||
'id', 'url', 'display', 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically',
|
||||
'choices_count', 'created', 'last_updated',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
|
||||
|
||||
|
||||
class CustomFieldSerializer(ValidatedModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
|
||||
content_types = ContentTypeField(
|
||||
@ -129,7 +150,8 @@ class CustomFieldSerializer(ValidatedModelSerializer):
|
||||
)
|
||||
filter_logic = ChoiceField(choices=CustomFieldFilterLogicChoices, required=False)
|
||||
data_type = serializers.SerializerMethodField()
|
||||
choice_set = NestedCustomFieldChoiceSetSerializer(
|
||||
choice_set = CustomFieldChoiceSetSerializer(
|
||||
nested=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
@ -168,28 +190,6 @@ class CustomFieldSerializer(ValidatedModelSerializer):
|
||||
return 'string'
|
||||
|
||||
|
||||
class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfieldchoiceset-detail')
|
||||
base_choices = ChoiceField(
|
||||
choices=CustomFieldChoiceSetBaseChoices,
|
||||
required=False
|
||||
)
|
||||
extra_choices = serializers.ListField(
|
||||
child=serializers.ListField(
|
||||
min_length=2,
|
||||
max_length=2
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CustomFieldChoiceSet
|
||||
fields = [
|
||||
'id', 'url', 'display', 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically',
|
||||
'choices_count', 'created', 'last_updated',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
|
||||
|
||||
|
||||
#
|
||||
# Custom links
|
||||
#
|
||||
@ -220,10 +220,12 @@ class ExportTemplateSerializer(ValidatedModelSerializer):
|
||||
queryset=ContentType.objects.with_feature('export_templates'),
|
||||
many=True
|
||||
)
|
||||
data_source = NestedDataSourceSerializer(
|
||||
data_source = DataSourceSerializer(
|
||||
nested=True,
|
||||
required=False
|
||||
)
|
||||
data_file = NestedDataFileSerializer(
|
||||
data_file = DataFileSerializer(
|
||||
nested=True,
|
||||
read_only=True
|
||||
)
|
||||
|
||||
@ -267,7 +269,7 @@ class BookmarkSerializer(ValidatedModelSerializer):
|
||||
queryset=ContentType.objects.with_feature('bookmarks'),
|
||||
)
|
||||
object = serializers.SerializerMethodField(read_only=True)
|
||||
user = NestedUserSerializer()
|
||||
user = UserSerializer(nested=True)
|
||||
|
||||
class Meta:
|
||||
model = Bookmark
|
||||
@ -482,10 +484,12 @@ class ConfigContextSerializer(ValidatedModelSerializer):
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
data_source = NestedDataSourceSerializer(
|
||||
data_source = DataSourceSerializer(
|
||||
nested=True,
|
||||
required=False
|
||||
)
|
||||
data_file = NestedDataFileSerializer(
|
||||
data_file = DataFileSerializer(
|
||||
nested=True,
|
||||
read_only=True
|
||||
)
|
||||
|
||||
@ -506,10 +510,12 @@ class ConfigContextSerializer(ValidatedModelSerializer):
|
||||
|
||||
class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configtemplate-detail')
|
||||
data_source = NestedDataSourceSerializer(
|
||||
data_source = DataSourceSerializer(
|
||||
nested=True,
|
||||
required=False
|
||||
)
|
||||
data_file = NestedDataFileSerializer(
|
||||
data_file = DataFileSerializer(
|
||||
nested=True,
|
||||
required=False
|
||||
)
|
||||
|
||||
@ -530,7 +536,7 @@ class ScriptSerializer(ValidatedModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:script-detail')
|
||||
description = serializers.SerializerMethodField(read_only=True)
|
||||
vars = serializers.SerializerMethodField(read_only=True)
|
||||
result = NestedJobSerializer(read_only=True)
|
||||
result = JobSerializer(nested=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Script
|
||||
@ -596,7 +602,8 @@ class ScriptInputSerializer(serializers.Serializer):
|
||||
|
||||
class ObjectChangeSerializer(BaseModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:objectchange-detail')
|
||||
user = NestedUserSerializer(
|
||||
user = UserSerializer(
|
||||
nested=True,
|
||||
read_only=True
|
||||
)
|
||||
action = ChoiceField(
|
||||
|
@ -2,29 +2,48 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerializer
|
||||
from dcim.api.serializers import DeviceSerializer, SiteSerializer
|
||||
from ipam.choices import *
|
||||
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES
|
||||
from ipam.models import *
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from tenancy.api.serializers import TenantSerializer
|
||||
from utilities.api import get_serializer_for_model
|
||||
from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
|
||||
from virtualization.api.serializers import VirtualMachineSerializer
|
||||
from vpn.api.nested_serializers import NestedL2VPNTerminationSerializer
|
||||
from .field_serializers import IPAddressField, IPNetworkField
|
||||
from .nested_serializers import *
|
||||
|
||||
|
||||
#
|
||||
# RIRs
|
||||
#
|
||||
|
||||
class RIRSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
|
||||
|
||||
# Related object counts
|
||||
aggregate_count = RelatedObjectCountField('aggregates')
|
||||
|
||||
class Meta:
|
||||
model = RIR
|
||||
fields = [
|
||||
'id', 'url', 'display', 'name', 'slug', 'is_private', 'description', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', 'aggregate_count',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'aggregate_count')
|
||||
|
||||
|
||||
#
|
||||
# ASN ranges
|
||||
#
|
||||
|
||||
class ASNRangeSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asnrange-detail')
|
||||
rir = NestedRIRSerializer()
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
rir = RIRSerializer(nested=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
asn_count = serializers.IntegerField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
@ -42,8 +61,8 @@ class ASNRangeSerializer(NetBoxModelSerializer):
|
||||
|
||||
class ASNSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
|
||||
rir = NestedRIRSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
rir = RIRSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
# Related object counts
|
||||
site_count = RelatedObjectCountField('sites')
|
||||
@ -66,7 +85,7 @@ class AvailableASNSerializer(serializers.Serializer):
|
||||
description = serializers.CharField(required=False)
|
||||
|
||||
def to_representation(self, asn):
|
||||
rir = NestedRIRSerializer(self.context['range'].rir, context={
|
||||
rir = RIRSerializer(self.context['range'].rir, nested=True, context={
|
||||
'request': self.context['request']
|
||||
}).data
|
||||
return {
|
||||
@ -81,7 +100,7 @@ class AvailableASNSerializer(serializers.Serializer):
|
||||
|
||||
class VRFSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
import_targets = SerializedPKRelatedField(
|
||||
queryset=RouteTarget.objects.all(),
|
||||
serializer=NestedRouteTargetSerializer,
|
||||
@ -109,13 +128,9 @@ class VRFSerializer(NetBoxModelSerializer):
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'rd', 'description', 'prefix_count')
|
||||
|
||||
|
||||
#
|
||||
# Route targets
|
||||
#
|
||||
|
||||
class RouteTargetSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = RouteTarget
|
||||
@ -127,29 +142,14 @@ class RouteTargetSerializer(NetBoxModelSerializer):
|
||||
|
||||
|
||||
#
|
||||
# RIRs/aggregates
|
||||
# Aggregates
|
||||
#
|
||||
|
||||
class RIRSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
|
||||
|
||||
# Related object counts
|
||||
aggregate_count = RelatedObjectCountField('aggregates')
|
||||
|
||||
class Meta:
|
||||
model = RIR
|
||||
fields = [
|
||||
'id', 'url', 'display', 'name', 'slug', 'is_private', 'description', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', 'aggregate_count',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'aggregate_count')
|
||||
|
||||
|
||||
class AggregateSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
|
||||
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
|
||||
rir = NestedRIRSerializer()
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
rir = RIRSerializer(nested=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
prefix = IPNetworkField()
|
||||
|
||||
class Meta:
|
||||
@ -180,7 +180,7 @@ class FHRPGroupSerializer(NetBoxModelSerializer):
|
||||
|
||||
class FHRPGroupAssignmentSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroupassignment-detail')
|
||||
group = NestedFHRPGroupSerializer()
|
||||
group = FHRPGroupSerializer(nested=True)
|
||||
interface_type = ContentTypeField(
|
||||
queryset=ContentType.objects.all()
|
||||
)
|
||||
@ -261,11 +261,11 @@ class VLANGroupSerializer(NetBoxModelSerializer):
|
||||
|
||||
class VLANSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
|
||||
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||
group = NestedVLANGroupSerializer(required=False, allow_null=True, default=None)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
||||
group = VLANGroupSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=VLANStatusChoices, required=False)
|
||||
role = NestedRoleSerializer(required=False, allow_null=True)
|
||||
role = RoleSerializer(nested=True, required=False, allow_null=True)
|
||||
l2vpn_termination = NestedL2VPNTerminationSerializer(read_only=True, allow_null=True)
|
||||
|
||||
# Related object counts
|
||||
@ -285,23 +285,24 @@ class AvailableVLANSerializer(serializers.Serializer):
|
||||
Representation of a VLAN which does not exist in the database.
|
||||
"""
|
||||
vid = serializers.IntegerField(read_only=True)
|
||||
group = NestedVLANGroupSerializer(read_only=True)
|
||||
group = VLANGroupSerializer(nested=True, read_only=True)
|
||||
|
||||
def to_representation(self, instance):
|
||||
return {
|
||||
'vid': instance,
|
||||
'group': NestedVLANGroupSerializer(
|
||||
'group': VLANGroupSerializer(
|
||||
self.context['group'],
|
||||
nested=True,
|
||||
context={'request': self.context['request']}
|
||||
).data,
|
||||
}
|
||||
|
||||
|
||||
class CreateAvailableVLANSerializer(NetBoxModelSerializer):
|
||||
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=VLANStatusChoices, required=False)
|
||||
role = NestedRoleSerializer(required=False, allow_null=True)
|
||||
role = RoleSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = VLAN
|
||||
@ -321,12 +322,12 @@ class CreateAvailableVLANSerializer(NetBoxModelSerializer):
|
||||
class PrefixSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
|
||||
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
|
||||
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
||||
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=PrefixStatusChoices, required=False)
|
||||
role = NestedRoleSerializer(required=False, allow_null=True)
|
||||
role = RoleSerializer(nested=True, required=False, allow_null=True)
|
||||
children = serializers.IntegerField(read_only=True)
|
||||
_depth = serializers.IntegerField(read_only=True)
|
||||
prefix = IPNetworkField()
|
||||
@ -374,11 +375,11 @@ class AvailablePrefixSerializer(serializers.Serializer):
|
||||
"""
|
||||
family = serializers.IntegerField(read_only=True)
|
||||
prefix = serializers.CharField(read_only=True)
|
||||
vrf = NestedVRFSerializer(read_only=True)
|
||||
vrf = VRFSerializer(nested=True, read_only=True)
|
||||
|
||||
def to_representation(self, instance):
|
||||
if self.context.get('vrf'):
|
||||
vrf = NestedVRFSerializer(self.context['vrf'], context={'request': self.context['request']}).data
|
||||
vrf = VRFSerializer(self.context['vrf'], nested=True, context={'request': self.context['request']}).data
|
||||
else:
|
||||
vrf = None
|
||||
return {
|
||||
@ -397,10 +398,10 @@ class IPRangeSerializer(NetBoxModelSerializer):
|
||||
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
|
||||
start_address = IPAddressField()
|
||||
end_address = IPAddressField()
|
||||
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=IPRangeStatusChoices, required=False)
|
||||
role = NestedRoleSerializer(required=False, allow_null=True)
|
||||
role = RoleSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = IPRange
|
||||
@ -420,8 +421,8 @@ class IPAddressSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
|
||||
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
|
||||
address = IPAddressField()
|
||||
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=IPAddressStatusChoices, required=False)
|
||||
role = ChoiceField(choices=IPAddressRoleChoices, allow_blank=True, required=False)
|
||||
assigned_object_type = ContentTypeField(
|
||||
@ -457,12 +458,12 @@ class AvailableIPSerializer(serializers.Serializer):
|
||||
"""
|
||||
family = serializers.IntegerField(read_only=True)
|
||||
address = serializers.CharField(read_only=True)
|
||||
vrf = NestedVRFSerializer(read_only=True)
|
||||
vrf = VRFSerializer(nested=True, read_only=True)
|
||||
description = serializers.CharField(required=False)
|
||||
|
||||
def to_representation(self, instance):
|
||||
if self.context.get('vrf'):
|
||||
vrf = NestedVRFSerializer(self.context['vrf'], context={'request': self.context['request']}).data
|
||||
vrf = VRFSerializer(self.context['vrf'], nested=True, context={'request': self.context['request']}).data
|
||||
else:
|
||||
vrf = None
|
||||
return {
|
||||
@ -491,8 +492,8 @@ class ServiceTemplateSerializer(NetBoxModelSerializer):
|
||||
|
||||
class ServiceSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
|
||||
device = NestedDeviceSerializer(required=False, allow_null=True)
|
||||
virtual_machine = NestedVirtualMachineSerializer(required=False, allow_null=True)
|
||||
device = DeviceSerializer(nested=True, required=False, allow_null=True)
|
||||
virtual_machine = VirtualMachineSerializer(nested=True, required=False, allow_null=True)
|
||||
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
|
||||
ipaddresses = SerializedPKRelatedField(
|
||||
queryset=IPAddress.objects.all(),
|
||||
|
@ -1,8 +1,9 @@
|
||||
from django.db.models import ManyToManyField
|
||||
from rest_framework import serializers
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
|
||||
from utilities.api import get_related_object_by_attrs
|
||||
|
||||
__all__ = (
|
||||
'BaseModelSerializer',
|
||||
'ValidatedModelSerializer',
|
||||
@ -12,15 +13,30 @@ __all__ = (
|
||||
class BaseModelSerializer(serializers.ModelSerializer):
|
||||
display = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
def __init__(self, *args, requested_fields=None, **kwargs):
|
||||
def __init__(self, *args, nested=False, requested_fields=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.nested = nested
|
||||
|
||||
if nested and not requested_fields:
|
||||
requested_fields = getattr(self.Meta, 'brief_fields', None)
|
||||
|
||||
# If specific fields have been requested, omit the others
|
||||
if requested_fields:
|
||||
for field in list(self.fields.keys()):
|
||||
if field not in requested_fields:
|
||||
self.fields.pop(field)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
|
||||
# If initialized as a nested serializer, we should expect to receive the attrs or PK
|
||||
# identifying a related object.
|
||||
if self.nested:
|
||||
queryset = self.Meta.model.objects.all()
|
||||
return get_related_object_by_attrs(queryset, data)
|
||||
|
||||
return super().to_internal_value(data)
|
||||
|
||||
@extend_schema_field(OpenApiTypes.STR)
|
||||
def get_display(self, obj):
|
||||
return str(obj)
|
||||
@ -32,6 +48,11 @@ class ValidatedModelSerializer(BaseModelSerializer):
|
||||
validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
|
||||
"""
|
||||
def validate(self, data):
|
||||
|
||||
# Skip validation if we're being used to represent a nested object
|
||||
if self.nested:
|
||||
return data
|
||||
|
||||
attrs = data.copy()
|
||||
|
||||
# Remove custom field data (if any) prior to model validation
|
||||
|
@ -1,10 +1,7 @@
|
||||
from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from extras.models import Tag
|
||||
from utilities.utils import dict_to_filter_params
|
||||
from utilities.api import get_related_object_by_attrs
|
||||
from .base import BaseModelSerializer
|
||||
|
||||
__all__ = (
|
||||
@ -20,43 +17,8 @@ class WritableNestedSerializer(BaseModelSerializer):
|
||||
subclassed to return a full representation of the related object on read.
|
||||
"""
|
||||
def to_internal_value(self, data):
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# Dictionary of related object attributes
|
||||
if isinstance(data, dict):
|
||||
params = dict_to_filter_params(data)
|
||||
queryset = self.Meta.model.objects
|
||||
try:
|
||||
return queryset.get(**params)
|
||||
except ObjectDoesNotExist:
|
||||
raise ValidationError(
|
||||
_("Related object not found using the provided attributes: {params}").format(params=params))
|
||||
except MultipleObjectsReturned:
|
||||
raise ValidationError(
|
||||
_("Multiple objects match the provided attributes: {params}").format(params=params)
|
||||
)
|
||||
except FieldError as e:
|
||||
raise ValidationError(e)
|
||||
|
||||
# Integer PK of related object
|
||||
try:
|
||||
# Cast as integer in case a PK was mistakenly sent as a string
|
||||
pk = int(data)
|
||||
except (TypeError, ValueError):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Related objects must be referenced by numeric ID or by dictionary of attributes. Received an "
|
||||
"unrecognized value: {value}"
|
||||
).format(value=data)
|
||||
)
|
||||
|
||||
# Look up object by PK
|
||||
try:
|
||||
return self.Meta.model.objects.get(pk=pk)
|
||||
except ObjectDoesNotExist:
|
||||
raise ValidationError(_("Related object not found using the provided numeric ID: {id}").format(id=pk))
|
||||
queryset = self.Meta.model.objects.all()
|
||||
return get_related_object_by_attrs(queryset, data)
|
||||
|
||||
|
||||
# Declared here for use by PrimaryModelSerializer, but should be imported from extras.api.nested_serializers
|
||||
|
@ -32,7 +32,7 @@ class TenantGroupSerializer(NestedGroupModelSerializer):
|
||||
|
||||
class TenantSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
|
||||
group = NestedTenantGroupSerializer(required=False, allow_null=True)
|
||||
group = TenantGroupSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
# Related object counts
|
||||
circuit_count = RelatedObjectCountField('circuits')
|
||||
@ -87,7 +87,7 @@ class ContactRoleSerializer(NetBoxModelSerializer):
|
||||
|
||||
class ContactSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contact-detail')
|
||||
group = NestedContactGroupSerializer(required=False, allow_null=True, default=None)
|
||||
group = ContactGroupSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||
|
||||
class Meta:
|
||||
model = Contact
|
||||
@ -104,8 +104,8 @@ class ContactAssignmentSerializer(NetBoxModelSerializer):
|
||||
queryset=ContentType.objects.all()
|
||||
)
|
||||
object = serializers.SerializerMethodField(read_only=True)
|
||||
contact = NestedContactSerializer()
|
||||
role = NestedContactRoleSerializer(required=False, allow_null=True)
|
||||
contact = ContactSerializer(nested=True)
|
||||
role = ContactRoleSerializer(nested=True, required=False, allow_null=True)
|
||||
priority = ChoiceField(choices=ContactPriorityChoices, allow_blank=True, required=False, default=lambda: '')
|
||||
|
||||
class Meta:
|
||||
|
@ -89,7 +89,7 @@ class TokenSerializer(ValidatedModelSerializer):
|
||||
required=False,
|
||||
write_only=not settings.ALLOW_TOKEN_RETRIEVAL
|
||||
)
|
||||
user = NestedUserSerializer()
|
||||
user = UserSerializer(nested=True)
|
||||
allowed_ips = serializers.ListField(
|
||||
child=IPNetworkSerializer(),
|
||||
required=False,
|
||||
@ -122,7 +122,8 @@ class TokenSerializer(ValidatedModelSerializer):
|
||||
|
||||
|
||||
class TokenProvisionSerializer(TokenSerializer):
|
||||
user = NestedUserSerializer(
|
||||
user = UserSerializer(
|
||||
nested=True,
|
||||
read_only=True
|
||||
)
|
||||
username = serializers.CharField(
|
||||
|
@ -3,23 +3,26 @@ import sys
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.core.exceptions import (
|
||||
FieldDoesNotExist, FieldError, MultipleObjectsReturned, ObjectDoesNotExist, ValidationError,
|
||||
)
|
||||
from django.db.models.fields.related import ManyToOneRel, RelatedField
|
||||
from django.http import JsonResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import status
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.utils import formatting
|
||||
|
||||
from netbox.api.fields import RelatedObjectCountField
|
||||
from netbox.api.exceptions import GraphQLTypeNotFound, SerializerNotFound
|
||||
from utilities.utils import count_related
|
||||
from .utils import dynamic_import
|
||||
from .utils import count_related, dict_to_filter_params, dynamic_import
|
||||
|
||||
__all__ = (
|
||||
'get_annotations_for_serializer',
|
||||
'get_graphql_type_for_model',
|
||||
'get_prefetches_for_serializer',
|
||||
'get_related_object_by_attrs',
|
||||
'get_serializer_for_model',
|
||||
'get_view_name',
|
||||
'is_api_request',
|
||||
@ -103,7 +106,7 @@ def get_prefetches_for_serializer(serializer_class, fields_to_include=None):
|
||||
"""
|
||||
model = serializer_class.Meta.model
|
||||
|
||||
# If specific fields are not specified, default to all
|
||||
# If fields are not specified, default to all
|
||||
if not fields_to_include:
|
||||
fields_to_include = serializer_class.Meta.fields
|
||||
|
||||
@ -128,7 +131,9 @@ def get_prefetches_for_serializer(serializer_class, fields_to_include=None):
|
||||
# for the related object.
|
||||
if serializer_field:
|
||||
if issubclass(type(serializer_field), Serializer):
|
||||
for subfield in get_prefetches_for_serializer(type(serializer_field)):
|
||||
# Determine which fields to prefetch for the nested object
|
||||
subfields = serializer_field.Meta.brief_fields if serializer_field.nested else None
|
||||
for subfield in get_prefetches_for_serializer(type(serializer_field), subfields):
|
||||
prefetch_fields.append(f'{field_name}__{subfield}')
|
||||
|
||||
return prefetch_fields
|
||||
@ -154,6 +159,48 @@ def get_annotations_for_serializer(serializer_class, fields_to_include=None):
|
||||
return annotations
|
||||
|
||||
|
||||
def get_related_object_by_attrs(queryset, attrs):
|
||||
"""
|
||||
Return an object identified by either a dictionary of attributes or its numeric primary key (ID). This is used
|
||||
for referencing related objects when creating/updating objects via the REST API.
|
||||
"""
|
||||
if attrs is None:
|
||||
return None
|
||||
|
||||
# Dictionary of related object attributes
|
||||
if isinstance(attrs, dict):
|
||||
params = dict_to_filter_params(attrs)
|
||||
try:
|
||||
return queryset.get(**params)
|
||||
except ObjectDoesNotExist:
|
||||
raise ValidationError(
|
||||
_("Related object not found using the provided attributes: {params}").format(params=params))
|
||||
except MultipleObjectsReturned:
|
||||
raise ValidationError(
|
||||
_("Multiple objects match the provided attributes: {params}").format(params=params)
|
||||
)
|
||||
except FieldError as e:
|
||||
raise ValidationError(e)
|
||||
|
||||
# Integer PK of related object
|
||||
try:
|
||||
# Cast as integer in case a PK was mistakenly sent as a string
|
||||
pk = int(attrs)
|
||||
except (TypeError, ValueError):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Related objects must be referenced by numeric ID or by dictionary of attributes. Received an "
|
||||
"unrecognized value: {value}"
|
||||
).format(value=attrs)
|
||||
)
|
||||
|
||||
# Look up object by PK
|
||||
try:
|
||||
return queryset.get(pk=pk)
|
||||
except ObjectDoesNotExist:
|
||||
raise ValidationError(_("Related object not found using the provided numeric ID: {id}").format(id=pk))
|
||||
|
||||
|
||||
def rest_api_server_error(request, *args, **kwargs):
|
||||
"""
|
||||
Handle exceptions and return a useful error message for REST API requests.
|
||||
|
@ -1,16 +1,14 @@
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from dcim.api.nested_serializers import (
|
||||
NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer,
|
||||
)
|
||||
from dcim.api.serializers import DeviceSerializer, DeviceRoleSerializer, PlatformSerializer, SiteSerializer
|
||||
from dcim.choices import InterfaceModeChoices
|
||||
from extras.api.nested_serializers import NestedConfigTemplateSerializer
|
||||
from extras.api.serializers import ConfigTemplateSerializer
|
||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer
|
||||
from ipam.models import VLAN
|
||||
from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from tenancy.api.serializers import TenantSerializer
|
||||
from virtualization.choices import *
|
||||
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualDisk, VirtualMachine, VMInterface
|
||||
from vpn.api.nested_serializers import NestedL2VPNTerminationSerializer
|
||||
@ -53,11 +51,11 @@ class ClusterGroupSerializer(NetBoxModelSerializer):
|
||||
|
||||
class ClusterSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
|
||||
type = NestedClusterTypeSerializer()
|
||||
group = NestedClusterGroupSerializer(required=False, allow_null=True, default=None)
|
||||
type = ClusterTypeSerializer(nested=True)
|
||||
group = ClusterGroupSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||
status = ChoiceField(choices=ClusterStatusChoices, required=False)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
site = NestedSiteSerializer(required=False, allow_null=True, default=None)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
site = SiteSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||
|
||||
# Related object counts
|
||||
device_count = RelatedObjectCountField('devices')
|
||||
@ -79,16 +77,16 @@ class ClusterSerializer(NetBoxModelSerializer):
|
||||
class VirtualMachineSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
|
||||
status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
|
||||
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||
cluster = NestedClusterSerializer(required=False, allow_null=True)
|
||||
device = NestedDeviceSerializer(required=False, allow_null=True)
|
||||
role = NestedDeviceRoleSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
||||
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
||||
cluster = ClusterSerializer(nested=True, required=False, allow_null=True)
|
||||
device = DeviceSerializer(nested=True, required=False, allow_null=True)
|
||||
role = DeviceRoleSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
platform = PlatformSerializer(nested=True, required=False, allow_null=True)
|
||||
primary_ip = NestedIPAddressSerializer(read_only=True)
|
||||
primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||
primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||
config_template = NestedConfigTemplateSerializer(required=False, allow_null=True, default=None)
|
||||
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||
|
||||
# Counter fields
|
||||
interface_count = serializers.IntegerField(read_only=True)
|
||||
@ -128,7 +126,7 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
|
||||
|
||||
class VMInterfaceSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
|
||||
virtual_machine = NestedVirtualMachineSerializer()
|
||||
virtual_machine = VirtualMachineSerializer(nested=True)
|
||||
parent = NestedVMInterfaceSerializer(required=False, allow_null=True)
|
||||
bridge = NestedVMInterfaceSerializer(required=False, allow_null=True)
|
||||
mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
|
||||
@ -178,7 +176,7 @@ class VMInterfaceSerializer(NetBoxModelSerializer):
|
||||
|
||||
class VirtualDiskSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualdisk-detail')
|
||||
virtual_machine = NestedVirtualMachineSerializer()
|
||||
virtual_machine = VirtualMachineSerializer(nested=True)
|
||||
|
||||
class Meta:
|
||||
model = VirtualDisk
|
||||
|
@ -2,12 +2,13 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedRouteTargetSerializer
|
||||
from ipam.api.serializers import IPAddressSerializer
|
||||
from ipam.api.nested_serializers import NestedRouteTargetSerializer
|
||||
from ipam.models import RouteTarget
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from tenancy.api.serializers import TenantSerializer
|
||||
from utilities.api import get_serializer_for_model
|
||||
from vpn.choices import *
|
||||
from vpn.models import *
|
||||
@ -27,90 +28,6 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class TunnelGroupSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
|
||||
|
||||
# Related object counts
|
||||
tunnel_count = RelatedObjectCountField('tunnels')
|
||||
|
||||
class Meta:
|
||||
model = TunnelGroup
|
||||
fields = [
|
||||
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
'tunnel_count',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tunnel_count')
|
||||
|
||||
|
||||
class TunnelSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunnel-detail'
|
||||
)
|
||||
status = ChoiceField(
|
||||
choices=TunnelStatusChoices
|
||||
)
|
||||
group = NestedTunnelGroupSerializer(
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
encapsulation = ChoiceField(
|
||||
choices=TunnelEncapsulationChoices
|
||||
)
|
||||
ipsec_profile = NestedIPSecProfileSerializer(
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
tenant = NestedTenantSerializer(
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
# Related object counts
|
||||
terminations_count = RelatedObjectCountField('terminations')
|
||||
|
||||
class Meta:
|
||||
model = Tunnel
|
||||
fields = (
|
||||
'id', 'url', 'display', 'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id',
|
||||
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'terminations_count',
|
||||
)
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||
|
||||
|
||||
class TunnelTerminationSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunneltermination-detail'
|
||||
)
|
||||
tunnel = NestedTunnelSerializer()
|
||||
role = ChoiceField(
|
||||
choices=TunnelTerminationRoleChoices
|
||||
)
|
||||
termination_type = ContentTypeField(
|
||||
queryset=ContentType.objects.all()
|
||||
)
|
||||
termination = serializers.SerializerMethodField(
|
||||
read_only=True
|
||||
)
|
||||
outside_ip = NestedIPAddressSerializer(
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TunnelTermination
|
||||
fields = (
|
||||
'id', 'url', 'display', 'tunnel', 'role', 'termination_type', 'termination_id', 'termination', 'outside_ip',
|
||||
'tags', 'custom_fields', 'created', 'last_updated',
|
||||
)
|
||||
brief_fields = ('id', 'url', 'display')
|
||||
|
||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||
def get_termination(self, obj):
|
||||
serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX)
|
||||
context = {'request': self.context['request']}
|
||||
return serializer(obj.termination, context=context).data
|
||||
|
||||
|
||||
class IKEProposalSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:ikeproposal-detail'
|
||||
@ -215,8 +132,12 @@ class IPSecProfileSerializer(NetBoxModelSerializer):
|
||||
mode = ChoiceField(
|
||||
choices=IPSecModeChoices
|
||||
)
|
||||
ike_policy = NestedIKEPolicySerializer()
|
||||
ipsec_policy = NestedIPSecPolicySerializer()
|
||||
ike_policy = IKEPolicySerializer(
|
||||
nested=True
|
||||
)
|
||||
ipsec_policy = IPSecPolicySerializer(
|
||||
nested=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IPSecProfile
|
||||
@ -227,6 +148,100 @@ class IPSecProfileSerializer(NetBoxModelSerializer):
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||
|
||||
|
||||
#
|
||||
# Tunnels
|
||||
#
|
||||
|
||||
class TunnelGroupSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
|
||||
|
||||
# Related object counts
|
||||
tunnel_count = RelatedObjectCountField('tunnels')
|
||||
|
||||
class Meta:
|
||||
model = TunnelGroup
|
||||
fields = [
|
||||
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
'tunnel_count',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tunnel_count')
|
||||
|
||||
|
||||
class TunnelSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunnel-detail'
|
||||
)
|
||||
status = ChoiceField(
|
||||
choices=TunnelStatusChoices
|
||||
)
|
||||
group = TunnelGroupSerializer(
|
||||
nested=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
encapsulation = ChoiceField(
|
||||
choices=TunnelEncapsulationChoices
|
||||
)
|
||||
ipsec_profile = IPSecProfileSerializer(
|
||||
nested=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
tenant = TenantSerializer(
|
||||
nested=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
# Related object counts
|
||||
terminations_count = RelatedObjectCountField('terminations')
|
||||
|
||||
class Meta:
|
||||
model = Tunnel
|
||||
fields = (
|
||||
'id', 'url', 'display', 'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id',
|
||||
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'terminations_count',
|
||||
)
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||
|
||||
|
||||
class TunnelTerminationSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name='vpn-api:tunneltermination-detail'
|
||||
)
|
||||
tunnel = TunnelSerializer(
|
||||
nested=True
|
||||
)
|
||||
role = ChoiceField(
|
||||
choices=TunnelTerminationRoleChoices
|
||||
)
|
||||
termination_type = ContentTypeField(
|
||||
queryset=ContentType.objects.all()
|
||||
)
|
||||
termination = serializers.SerializerMethodField(
|
||||
read_only=True
|
||||
)
|
||||
outside_ip = IPAddressSerializer(
|
||||
nested=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TunnelTermination
|
||||
fields = (
|
||||
'id', 'url', 'display', 'tunnel', 'role', 'termination_type', 'termination_id', 'termination', 'outside_ip',
|
||||
'tags', 'custom_fields', 'created', 'last_updated',
|
||||
)
|
||||
brief_fields = ('id', 'url', 'display')
|
||||
|
||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||
def get_termination(self, obj):
|
||||
serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX)
|
||||
context = {'request': self.context['request']}
|
||||
return serializer(obj.termination, context=context).data
|
||||
|
||||
|
||||
#
|
||||
# L2VPN
|
||||
#
|
||||
@ -246,7 +261,7 @@ class L2VPNSerializer(NetBoxModelSerializer):
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = L2VPN
|
||||
@ -259,7 +274,9 @@ class L2VPNSerializer(NetBoxModelSerializer):
|
||||
|
||||
class L2VPNTerminationSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:l2vpntermination-detail')
|
||||
l2vpn = NestedL2VPNSerializer()
|
||||
l2vpn = L2VPNSerializer(
|
||||
nested=True
|
||||
)
|
||||
assigned_object_type = ContentTypeField(
|
||||
queryset=ContentType.objects.all()
|
||||
)
|
||||
|
@ -1,11 +1,11 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from dcim.api.serializers import NestedInterfaceSerializer
|
||||
from ipam.api.serializers import NestedVLANSerializer
|
||||
from dcim.api.serializers import InterfaceSerializer
|
||||
from ipam.api.serializers import VLANSerializer
|
||||
from netbox.api.fields import ChoiceField
|
||||
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
|
||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||
from tenancy.api.serializers import TenantSerializer
|
||||
from wireless.choices import *
|
||||
from wireless.models import *
|
||||
from .nested_serializers import *
|
||||
@ -33,10 +33,10 @@ class WirelessLANGroupSerializer(NestedGroupModelSerializer):
|
||||
|
||||
class WirelessLANSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
|
||||
group = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
|
||||
group = WirelessLANGroupSerializer(nested=True, required=False, allow_null=True)
|
||||
status = ChoiceField(choices=WirelessLANStatusChoices, required=False, allow_blank=True)
|
||||
vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
|
||||
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
|
||||
|
||||
@ -52,9 +52,9 @@ class WirelessLANSerializer(NetBoxModelSerializer):
|
||||
class WirelessLinkSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
|
||||
status = ChoiceField(choices=LinkStatusChoices, required=False)
|
||||
interface_a = NestedInterfaceSerializer()
|
||||
interface_b = NestedInterfaceSerializer()
|
||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||
interface_a = InterfaceSerializer(nested=True)
|
||||
interface_b = InterfaceSerializer(nested=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
|
||||
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
|
||||
|
||||
|
Reference in New Issue
Block a user