Coverage for dcim/api/serializers_/cables.py: 75%

79 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1from drf_spectacular.types import OpenApiTypes 

2from drf_spectacular.utils import extend_schema_field 

3from rest_framework import serializers 

4 

5from dcim.choices import * 

6from dcim.models import Cable, CableBundle, CablePath, CableTermination 

7from netbox.api.fields import ChoiceField, ContentTypeField 

8from netbox.api.gfk_fields import GFKSerializerField 

9from netbox.api.serializers import ( 

10 BaseModelSerializer, 

11 GenericObjectSerializer, 

12 NetBoxModelSerializer, 

13 PrimaryModelSerializer, 

14) 

15from tenancy.api.serializers_.tenants import TenantSerializer 

16from utilities.api import get_serializer_for_model 

17 

18__all__ = ( 

19 'CableBundleSerializer', 

20 'CablePathSerializer', 

21 'CableSerializer', 

22 'CableTerminationSerializer', 

23 'CabledObjectSerializer', 

24 'TracedCableSerializer', 

25) 

26 

27 

28class CableBundleSerializer(PrimaryModelSerializer): 

29 cable_count = serializers.IntegerField(read_only=True, default=0) 

30 

31 class Meta: 

32 model = CableBundle 

33 fields = [ 

34 'id', 'url', 'display_url', 'display', 'name', 'description', 'owner', 'comments', 'tags', 

35 'custom_fields', 'created', 'last_updated', 'cable_count', 

36 ] 

37 brief_fields = ('id', 'url', 'display', 'name', 'description') 

38 

39 

40class CableSerializer(PrimaryModelSerializer): 

41 a_terminations = GenericObjectSerializer(many=True, required=False) 

42 b_terminations = GenericObjectSerializer(many=True, required=False) 

43 status = ChoiceField(choices=LinkStatusChoices, required=False) 

44 profile = ChoiceField(choices=CableProfileChoices, required=False) 

45 tenant = TenantSerializer(nested=True, required=False, allow_null=True) 

46 length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False, allow_null=True) 

47 bundle = CableBundleSerializer(nested=True, required=False, allow_null=True, default=None) 

48 

49 class Meta: 

50 model = Cable 

51 fields = [ 

52 'id', 'url', 'display_url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'profile', 

53 'tenant', 'bundle', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags', 

54 'custom_fields', 'created', 'last_updated', 

55 ] 

56 brief_fields = ('id', 'url', 'display', 'label', 'description') 

57 

58 

59class TracedCableSerializer(BaseModelSerializer): 

60 """ 

61 Used only while tracing a cable path. 

62 """ 

63 

64 class Meta: 

65 model = Cable 

66 fields = [ 

67 'id', 'url', 'display_url', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'description', 

68 ] 

69 

70 

71class CableTerminationSerializer(NetBoxModelSerializer): 

72 termination_type = ContentTypeField( 

73 read_only=True, 

74 ) 

75 termination = GFKSerializerField(read_only=True) 

76 

77 class Meta: 

78 model = CableTermination 

79 fields = [ 

80 'id', 'url', 'display', 'cable', 'cable_end', 'termination_type', 'termination_id', 

81 'termination', 'connector', 'positions', 'created', 'last_updated', 

82 ] 

83 read_only_fields = fields 

84 brief_fields = ( 

85 'id', 'url', 'display', 'cable', 'cable_end', 'connector', 'positions', 'termination_type', 

86 'termination_id', 

87 ) 

88 

89 

90class CablePathSerializer(serializers.ModelSerializer): 

91 path = serializers.SerializerMethodField(read_only=True) 

92 

93 class Meta: 

94 model = CablePath 

95 fields = ['id', 'path', 'is_active', 'is_complete', 'is_split'] 

96 

97 @extend_schema_field(serializers.ListField) 

98 def get_path(self, obj): 

99 ret = [] 

100 for nodes in obj.path_objects: 

101 if not nodes: 

102 # The path contains an invalid object 

103 return [] 

104 serializer = get_serializer_for_model(nodes[0]) 

105 context = {'request': self.context['request']} 

106 ret.append(serializer(nodes, nested=True, many=True, context=context).data) 

107 return ret 

108 

109 

110class CabledObjectSerializer(serializers.ModelSerializer): 

111 cable = CableSerializer(nested=True, read_only=True, allow_null=True) 

112 # Use DRF's ChoiceField; NetBox's ChoiceField would return a value/label object. 

113 cable_end = serializers.ChoiceField(choices=CableEndChoices, read_only=True, allow_null=True) 

114 link_peers_type = serializers.SerializerMethodField(read_only=True, allow_null=True) 

115 link_peers = serializers.SerializerMethodField(read_only=True) 

116 _occupied = serializers.SerializerMethodField(read_only=True) 

117 

118 @extend_schema_field(OpenApiTypes.STR) 

119 def get_link_peers_type(self, obj): 

120 """ 

121 Return the type of the peer link terminations, or None. 

122 """ 

123 if not obj.cable: 123 ↛ 126line 123 didn't jump to line 126 because the condition on line 123 was always true

124 return None 

125 

126 if obj.link_peers: 

127 return f'{obj.link_peers[0]._meta.app_label}.{obj.link_peers[0]._meta.model_name}' 

128 

129 return None 

130 

131 @extend_schema_field(serializers.ListField) 

132 def get_link_peers(self, obj): 

133 """ 

134 Return the appropriate serializer for the link termination model. 

135 """ 

136 if not obj.link_peers: 136 ↛ 140line 136 didn't jump to line 140 because the condition on line 136 was always true

137 return [] 

138 

139 # Return serialized peer termination objects 

140 serializer = get_serializer_for_model(obj.link_peers[0]) 

141 context = {'request': self.context['request']} 

142 return serializer(obj.link_peers, nested=True, many=True, context=context).data 

143 

144 @extend_schema_field(serializers.BooleanField) 

145 def get__occupied(self, obj): 

146 return obj._occupied