mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
DeviceType.subdevice_role to slug (#3569)
This commit is contained in:
@ -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)
|
||||
|
||||
|
@ -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
|
||||
#
|
||||
|
@ -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
|
||||
#
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
),
|
||||
]
|
@ -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):
|
||||
|
@ -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,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'
|
||||
|
Reference in New Issue
Block a user