Kaynağa Gözat

Fixes #9330: Add missing module_type field to REST API serializers for modular device component templates

jeremystretch 3 yıl önce
ebeveyn
işleme
bdb21da26e

+ 1 - 0
docs/release-notes/version-3.2.md

@@ -17,6 +17,7 @@
 * [#9306](https://github.com/netbox-community/netbox/issues/9306) - Include VC master interfaces when selecting a LAG/bridge for a VC member interface
 * [#9311](https://github.com/netbox-community/netbox/issues/9311) - Permit creating contact assignment without a priority via the REST API
 * [#9313](https://github.com/netbox-community/netbox/issues/9313) - Remove HTML code from CSV output of many-to-many relationships
+* [#9330](https://github.com/netbox-community/netbox/issues/9330) - Add missing `module_type` field to REST API serializers for modular device component templates
 
 ---
 

+ 84 - 19
netbox/dcim/api/serializers.py

@@ -315,7 +315,16 @@ class ModuleTypeSerializer(NetBoxModelSerializer):
 
 class ConsolePortTemplateSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleporttemplate-detail')
-    device_type = NestedDeviceTypeSerializer()
+    device_type = NestedDeviceTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
+    module_type = NestedModuleTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
     type = ChoiceField(
         choices=ConsolePortTypeChoices,
         allow_blank=True,
@@ -325,13 +334,23 @@ class ConsolePortTemplateSerializer(ValidatedModelSerializer):
     class Meta:
         model = ConsolePortTemplate
         fields = [
-            'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated',
+            'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'description', 'created',
+            'last_updated',
         ]
 
 
 class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverporttemplate-detail')
-    device_type = NestedDeviceTypeSerializer()
+    device_type = NestedDeviceTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
+    module_type = NestedModuleTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
     type = ChoiceField(
         choices=ConsolePortTypeChoices,
         allow_blank=True,
@@ -341,13 +360,23 @@ class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
     class Meta:
         model = ConsoleServerPortTemplate
         fields = [
-            'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated',
+            'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'description', 'created',
+            'last_updated',
         ]
 
 
 class PowerPortTemplateSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerporttemplate-detail')
-    device_type = NestedDeviceTypeSerializer()
+    device_type = NestedDeviceTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
+    module_type = NestedModuleTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
     type = ChoiceField(
         choices=PowerPortTypeChoices,
         allow_blank=True,
@@ -357,14 +386,23 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer):
     class Meta:
         model = PowerPortTemplate
         fields = [
-            'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
-            'description', 'created', 'last_updated',
+            'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'maximum_draw',
+            'allocated_draw', 'description', 'created', 'last_updated',
         ]
 
 
 class PowerOutletTemplateSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlettemplate-detail')
-    device_type = NestedDeviceTypeSerializer()
+    device_type = NestedDeviceTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
+    module_type = NestedModuleTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
     type = ChoiceField(
         choices=PowerOutletTypeChoices,
         allow_blank=True,
@@ -383,48 +421,75 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer):
     class Meta:
         model = PowerOutletTemplate
         fields = [
-            'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
-            'created', 'last_updated',
+            'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg',
+            'description', 'created', 'last_updated',
         ]
 
 
 class InterfaceTemplateSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfacetemplate-detail')
-    device_type = NestedDeviceTypeSerializer()
+    device_type = NestedDeviceTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
+    module_type = NestedModuleTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
     type = ChoiceField(choices=InterfaceTypeChoices)
 
     class Meta:
         model = InterfaceTemplate
         fields = [
-            'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description', 'created',
-            'last_updated',
+            'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'description',
+            'created', 'last_updated',
         ]
 
 
 class RearPortTemplateSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
-    device_type = NestedDeviceTypeSerializer()
+    device_type = NestedDeviceTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
+    module_type = NestedModuleTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
     type = ChoiceField(choices=PortTypeChoices)
 
     class Meta:
         model = RearPortTemplate
         fields = [
-            'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description',
-            'created', 'last_updated',
+            'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'positions',
+            'description', 'created', 'last_updated',
         ]
 
 
 class FrontPortTemplateSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
-    device_type = NestedDeviceTypeSerializer()
+    device_type = NestedDeviceTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
+    module_type = NestedModuleTypeSerializer(
+        required=False,
+        allow_null=True,
+        default=None
+    )
     type = ChoiceField(choices=PortTypeChoices)
     rear_port = NestedRearPortTemplateSerializer()
 
     class Meta:
         model = FrontPortTemplate
         fields = [
-            'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position',
-            'description', 'created', 'last_updated',
+            'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'rear_port',
+            'rear_port_position', 'description', 'created', 'last_updated',
         ]
 
 

+ 82 - 16
netbox/dcim/tests/test_api.py

@@ -523,6 +523,9 @@ class ConsolePortTemplateTest(APIViewTestCases.APIViewTestCase):
         devicetype = DeviceType.objects.create(
             manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
         )
+        moduletype = ModuleType.objects.create(
+            manufacturer=manufacturer, model='Module Type 1'
+        )
 
         console_port_templates = (
             ConsolePortTemplate(device_type=devicetype, name='Console Port Template 1'),
@@ -541,9 +544,13 @@ class ConsolePortTemplateTest(APIViewTestCases.APIViewTestCase):
                 'name': 'Console Port Template 5',
             },
             {
-                'device_type': devicetype.pk,
+                'module_type': moduletype.pk,
                 'name': 'Console Port Template 6',
             },
+            {
+                'module_type': moduletype.pk,
+                'name': 'Console Port Template 7',
+            },
         ]
 
 
@@ -560,6 +567,9 @@ class ConsoleServerPortTemplateTest(APIViewTestCases.APIViewTestCase):
         devicetype = DeviceType.objects.create(
             manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
         )
+        moduletype = ModuleType.objects.create(
+            manufacturer=manufacturer, model='Module Type 1'
+        )
 
         console_server_port_templates = (
             ConsoleServerPortTemplate(device_type=devicetype, name='Console Server Port Template 1'),
@@ -578,9 +588,13 @@ class ConsoleServerPortTemplateTest(APIViewTestCases.APIViewTestCase):
                 'name': 'Console Server Port Template 5',
             },
             {
-                'device_type': devicetype.pk,
+                'module_type': moduletype.pk,
                 'name': 'Console Server Port Template 6',
             },
+            {
+                'module_type': moduletype.pk,
+                'name': 'Console Server Port Template 7',
+            },
         ]
 
 
@@ -597,6 +611,9 @@ class PowerPortTemplateTest(APIViewTestCases.APIViewTestCase):
         devicetype = DeviceType.objects.create(
             manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
         )
+        moduletype = ModuleType.objects.create(
+            manufacturer=manufacturer, model='Module Type 1'
+        )
 
         power_port_templates = (
             PowerPortTemplate(device_type=devicetype, name='Power Port Template 1'),
@@ -615,9 +632,13 @@ class PowerPortTemplateTest(APIViewTestCases.APIViewTestCase):
                 'name': 'Power Port Template 5',
             },
             {
-                'device_type': devicetype.pk,
+                'module_type': moduletype.pk,
                 'name': 'Power Port Template 6',
             },
+            {
+                'module_type': moduletype.pk,
+                'name': 'Power Port Template 7',
+            },
         ]
 
 
@@ -634,6 +655,9 @@ class PowerOutletTemplateTest(APIViewTestCases.APIViewTestCase):
         devicetype = DeviceType.objects.create(
             manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
         )
+        moduletype = ModuleType.objects.create(
+            manufacturer=manufacturer, model='Module Type 1'
+        )
 
         power_port_templates = (
             PowerPortTemplate(device_type=devicetype, name='Power Port Template 1'),
@@ -664,6 +688,14 @@ class PowerOutletTemplateTest(APIViewTestCases.APIViewTestCase):
                 'name': 'Power Outlet Template 6',
                 'power_port': None,
             },
+            {
+                'module_type': moduletype.pk,
+                'name': 'Power Outlet Template 7',
+            },
+            {
+                'module_type': moduletype.pk,
+                'name': 'Power Outlet Template 8',
+            },
         ]
 
 
@@ -680,6 +712,9 @@ class InterfaceTemplateTest(APIViewTestCases.APIViewTestCase):
         devicetype = DeviceType.objects.create(
             manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
         )
+        moduletype = ModuleType.objects.create(
+            manufacturer=manufacturer, model='Module Type 1'
+        )
 
         interface_templates = (
             InterfaceTemplate(device_type=devicetype, name='Interface Template 1', type='1000base-t'),
@@ -700,10 +735,15 @@ class InterfaceTemplateTest(APIViewTestCases.APIViewTestCase):
                 'type': '1000base-t',
             },
             {
-                'device_type': devicetype.pk,
+                'module_type': moduletype.pk,
                 'name': 'Interface Template 6',
                 'type': '1000base-t',
             },
+            {
+                'module_type': moduletype.pk,
+                'name': 'Interface Template 7',
+                'type': '1000base-t',
+            },
         ]
 
 
@@ -720,14 +760,19 @@ class FrontPortTemplateTest(APIViewTestCases.APIViewTestCase):
         devicetype = DeviceType.objects.create(
             manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
         )
+        moduletype = ModuleType.objects.create(
+            manufacturer=manufacturer, model='Module Type 1'
+        )
 
         rear_port_templates = (
             RearPortTemplate(device_type=devicetype, name='Rear Port Template 1', type=PortTypeChoices.TYPE_8P8C),
             RearPortTemplate(device_type=devicetype, name='Rear Port Template 2', type=PortTypeChoices.TYPE_8P8C),
             RearPortTemplate(device_type=devicetype, name='Rear Port Template 3', type=PortTypeChoices.TYPE_8P8C),
             RearPortTemplate(device_type=devicetype, name='Rear Port Template 4', type=PortTypeChoices.TYPE_8P8C),
-            RearPortTemplate(device_type=devicetype, name='Rear Port Template 5', type=PortTypeChoices.TYPE_8P8C),
-            RearPortTemplate(device_type=devicetype, name='Rear Port Template 6', type=PortTypeChoices.TYPE_8P8C),
+            RearPortTemplate(module_type=moduletype, name='Rear Port Template 5', type=PortTypeChoices.TYPE_8P8C),
+            RearPortTemplate(module_type=moduletype, name='Rear Port Template 6', type=PortTypeChoices.TYPE_8P8C),
+            RearPortTemplate(module_type=moduletype, name='Rear Port Template 7', type=PortTypeChoices.TYPE_8P8C),
+            RearPortTemplate(module_type=moduletype, name='Rear Port Template 8', type=PortTypeChoices.TYPE_8P8C),
         )
         RearPortTemplate.objects.bulk_create(rear_port_templates)
 
@@ -745,15 +790,28 @@ class FrontPortTemplateTest(APIViewTestCases.APIViewTestCase):
                 rear_port=rear_port_templates[1]
             ),
             FrontPortTemplate(
-                device_type=devicetype,
-                name='Front Port Template 3',
+                module_type=moduletype,
+                name='Front Port Template 5',
+                type=PortTypeChoices.TYPE_8P8C,
+                rear_port=rear_port_templates[4]
+            ),
+            FrontPortTemplate(
+                module_type=moduletype,
+                name='Front Port Template 6',
                 type=PortTypeChoices.TYPE_8P8C,
-                rear_port=rear_port_templates[2]
+                rear_port=rear_port_templates[5]
             ),
         )
         FrontPortTemplate.objects.bulk_create(front_port_templates)
 
         cls.create_data = [
+            {
+                'device_type': devicetype.pk,
+                'name': 'Front Port Template 3',
+                'type': PortTypeChoices.TYPE_8P8C,
+                'rear_port': rear_port_templates[2].pk,
+                'rear_port_position': 1,
+            },
             {
                 'device_type': devicetype.pk,
                 'name': 'Front Port Template 4',
@@ -762,17 +820,17 @@ class FrontPortTemplateTest(APIViewTestCases.APIViewTestCase):
                 'rear_port_position': 1,
             },
             {
-                'device_type': devicetype.pk,
-                'name': 'Front Port Template 5',
+                'module_type': moduletype.pk,
+                'name': 'Front Port Template 7',
                 'type': PortTypeChoices.TYPE_8P8C,
-                'rear_port': rear_port_templates[4].pk,
+                'rear_port': rear_port_templates[6].pk,
                 'rear_port_position': 1,
             },
             {
-                'device_type': devicetype.pk,
-                'name': 'Front Port Template 6',
+                'module_type': moduletype.pk,
+                'name': 'Front Port Template 8',
                 'type': PortTypeChoices.TYPE_8P8C,
-                'rear_port': rear_port_templates[5].pk,
+                'rear_port': rear_port_templates[7].pk,
                 'rear_port_position': 1,
             },
         ]
@@ -791,6 +849,9 @@ class RearPortTemplateTest(APIViewTestCases.APIViewTestCase):
         devicetype = DeviceType.objects.create(
             manufacturer=manufacturer, model='Device Type 1', slug='device-type-1'
         )
+        moduletype = ModuleType.objects.create(
+            manufacturer=manufacturer, model='Module Type 1'
+        )
 
         rear_port_templates = (
             RearPortTemplate(device_type=devicetype, name='Rear Port Template 1', type=PortTypeChoices.TYPE_8P8C),
@@ -811,10 +872,15 @@ class RearPortTemplateTest(APIViewTestCases.APIViewTestCase):
                 'type': PortTypeChoices.TYPE_8P8C,
             },
             {
-                'device_type': devicetype.pk,
+                'module_type': moduletype.pk,
                 'name': 'Rear Port Template 6',
                 'type': PortTypeChoices.TYPE_8P8C,
             },
+            {
+                'module_type': moduletype.pk,
+                'name': 'Rear Port Template 7',
+                'type': PortTypeChoices.TYPE_8P8C,
+            },
         ]