Browse Source

DeviceType.subdevice_role to slug (#3569)

Jeremy Stretch 6 năm trước cách đây
mục cha
commit
afd82fd9d3

+ 1 - 1
netbox/dcim/api/serializers.py

@@ -187,7 +187,7 @@ class ManufacturerSerializer(ValidatedModelSerializer):
 
 class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
     manufacturer = NestedManufacturerSerializer()
-    subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
+    subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, required=False, allow_null=True)
     tags = TagListSerializerField(required=False)
     device_count = serializers.IntegerField(read_only=True)
 

+ 20 - 0
netbox/dcim/choices.py

@@ -66,6 +66,26 @@ class RackStatusChoices(ChoiceSet):
     }
 
 
+#
+# DeviceTypes
+#
+
+class SubdeviceRoleChoices(ChoiceSet):
+
+    ROLE_PARENT = 'parent'
+    ROLE_CHILD = 'child'
+
+    CHOICES = (
+        (ROLE_PARENT, 'Parent'),
+        (ROLE_CHILD, 'Child'),
+    )
+
+    LEGACY_MAP = {
+        ROLE_PARENT: True,
+        ROLE_CHILD: False,
+    }
+
+
 #
 # Devices
 #

+ 0 - 9
netbox/dcim/constants.py

@@ -1,12 +1,3 @@
-# Parent/child device roles
-SUBDEVICE_ROLE_PARENT = True
-SUBDEVICE_ROLE_CHILD = False
-SUBDEVICE_ROLE_CHOICES = (
-    (None, 'None'),
-    (SUBDEVICE_ROLE_PARENT, 'Parent'),
-    (SUBDEVICE_ROLE_CHILD, 'Child'),
-)
-
 #
 # Numeric interface types
 #

+ 4 - 6
netbox/dcim/forms.py

@@ -879,12 +879,10 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldFilterForm):
             value_field="slug",
         )
     )
-    subdevice_role = forms.NullBooleanField(
+    subdevice_role = forms.MultipleChoiceField(
+        choices=add_blank_choice(SubdeviceRoleChoices),
         required=False,
-        label='Subdevice role',
-        widget=StaticSelect2(
-            choices=add_blank_choice(SUBDEVICE_ROLE_CHOICES)
-        )
+        widget=StaticSelect2Multiple()
     )
     console_ports = forms.NullBooleanField(
         required=False,
@@ -3382,7 +3380,7 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
             rack=device_bay.device.rack,
             parent_bay__isnull=True,
             device_type__u_height=0,
-            device_type__subdevice_role=SUBDEVICE_ROLE_CHILD
+            device_type__subdevice_role=SubdeviceRoleChoices.ROLE_CHILD
         ).exclude(pk=device_bay.device.pk)
 
 

+ 31 - 0
netbox/dcim/migrations/0081_devicetype_subdevicerole_to_slug.py

@@ -0,0 +1,31 @@
+from django.db import migrations, models
+
+SUBDEVICE_ROLE_CHOICES = (
+    ('true', 'parent'),
+    ('false', 'child'),
+)
+
+
+def devicetype_subdevicerole_to_slug(apps, schema_editor):
+    DeviceType = apps.get_model('dcim', 'DeviceType')
+    for boolean, slug in SUBDEVICE_ROLE_CHOICES:
+        DeviceType.objects.filter(subdevice_role=boolean).update(subdevice_role=slug)
+
+
+class Migration(migrations.Migration):
+    atomic = False
+
+    dependencies = [
+        ('dcim', '0080_device_face_to_slug'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='devicetype',
+            name='subdevice_role',
+            field=models.CharField(blank=True, default='', max_length=50),
+        ),
+        migrations.RunPython(
+            code=devicetype_subdevicerole_to_slug
+        ),
+    ]

+ 12 - 10
netbox/dcim/models.py

@@ -919,12 +919,12 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
         verbose_name='Is full depth',
         help_text='Device consumes both front and rear rack faces'
     )
-    subdevice_role = models.NullBooleanField(
-        default=None,
+    subdevice_role = models.CharField(
+        max_length=50,
+        choices=SubdeviceRoleChoices,
         verbose_name='Parent/child status',
-        choices=SUBDEVICE_ROLE_CHOICES,
-        help_text='Parent devices house child devices in device bays. Select '
-                  '"None" if this device type is neither a parent nor a child.'
+        help_text='Parent devices house child devices in device bays. Leave blank '
+                  'if this device type is neither a parent nor a child.'
     )
     comments = models.TextField(
         blank=True
@@ -968,7 +968,7 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
             self.part_number,
             self.u_height,
             self.is_full_depth,
-            self.get_subdevice_role_display() if self.subdevice_role else None,
+            self.get_subdevice_role_display(),
             self.comments,
         )
 
@@ -988,13 +988,15 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
                                     "{}U".format(d, d.rack, self.u_height)
                     })
 
-        if self.subdevice_role != SUBDEVICE_ROLE_PARENT and self.device_bay_templates.count():
+        if (
+                self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT
+        ) and self.device_bay_templates.count():
             raise ValidationError({
                 'subdevice_role': "Must delete all device bay templates associated with this device before "
                                   "declassifying it as a parent device."
             })
 
-        if self.u_height and self.subdevice_role == SUBDEVICE_ROLE_CHILD:
+        if self.u_height and self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD:
             raise ValidationError({
                 'u_height': "Child device types must be 0U."
             })
@@ -1005,11 +1007,11 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
 
     @property
     def is_parent_device(self):
-        return bool(self.subdevice_role)
+        return self.subdevice_role == SubdeviceRoleChoices.ROLE_PARENT
 
     @property
     def is_child_device(self):
-        return bool(self.subdevice_role is False)
+        return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD
 
 
 class ConsolePortTemplate(ComponentTemplateModel):

+ 0 - 8
netbox/dcim/tables.py

@@ -156,10 +156,6 @@ DEVICE_PRIMARY_IP = """
 {{ record.primary_ip4.address.ip|default:"" }}
 """
 
-SUBDEVICE_ROLE_TEMPLATE = """
-{% if record.subdevice_role == True %}Parent{% elif record.subdevice_role == False %}Child{% else %}—{% endif %}
-"""
-
 DEVICETYPE_INSTANCES_TEMPLATE = """
 <a href="{% url 'dcim:device_list' %}?manufacturer_id={{ record.manufacturer_id }}&device_type_id={{ record.pk }}">{{ record.instance_count }}</a>
 """
@@ -391,10 +387,6 @@ class DeviceTypeTable(BaseTable):
         verbose_name='Device Type'
     )
     is_full_depth = BooleanColumn(verbose_name='Full Depth')
-    subdevice_role = tables.TemplateColumn(
-        template_code=SUBDEVICE_ROLE_TEMPLATE,
-        verbose_name='Subdevice Role'
-    )
     instance_count = tables.TemplateColumn(
         template_code=DEVICETYPE_INSTANCES_TEMPLATE,
         verbose_name='Instances'

+ 3 - 2
netbox/dcim/tests/test_api.py

@@ -3,6 +3,7 @@ from netaddr import IPNetwork
 from rest_framework import status
 
 from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
+from dcim.choices import SubdeviceRoleChoices
 from dcim.constants import *
 from dcim.models import (
     Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
@@ -2590,11 +2591,11 @@ class DeviceBayTest(APITestCase):
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         self.devicetype1 = DeviceType.objects.create(
             manufacturer=manufacturer, model='Parent Device Type', slug='parent-device-type',
-            subdevice_role=SUBDEVICE_ROLE_PARENT
+            subdevice_role=SubdeviceRoleChoices.ROLE_PARENT
         )
         self.devicetype2 = DeviceType.objects.create(
             manufacturer=manufacturer, model='Child Device Type', slug='child-device-type',
-            subdevice_role=SUBDEVICE_ROLE_CHILD
+            subdevice_role=SubdeviceRoleChoices.ROLE_CHILD
         )
         devicerole = DeviceRole.objects.create(
             name='Test Device Role 1', slug='test-device-role-1', color='ff0000'