diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 3c4170373..c6ca24905 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -585,7 +585,7 @@ class CableSerializer(ValidatedModelSerializer): ) termination_a = serializers.SerializerMethodField(read_only=True) termination_b = serializers.SerializerMethodField(read_only=True) - status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False) + status = ChoiceField(choices=CableStatusChoices, required=False) length_unit = ChoiceField(choices=CableLengthUnitChoices, required=False, allow_null=True) class Meta: diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index f9265480b..408e49888 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -923,6 +923,22 @@ class CableTypeChoices(ChoiceSet): } +class CableStatusChoices(ChoiceSet): + + STATUS_CONNECTED = 'connected' + STATUS_PLANNED = 'planned' + + CHOICES = ( + (STATUS_CONNECTED, 'Connected'), + (STATUS_PLANNED, 'Planned'), + ) + + LEGACY_MAP = { + STATUS_CONNECTED: True, + STATUS_PLANNED: False, + } + + class CableLengthUnitChoices(ChoiceSet): UNIT_METER = 'm' diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 85a1b4cbc..1d76a0bc0 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -983,7 +983,7 @@ class CableFilter(django_filters.FilterSet): choices=CableTypeChoices ) status = django_filters.MultipleChoiceFilter( - choices=CONNECTION_STATUS_CHOICES + choices=CableStatusChoices ) color = django_filters.MultipleChoiceFilter( choices=COLOR_CHOICES diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 9a836326a..831bb7c81 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -3427,7 +3427,7 @@ class CableCSVForm(forms.ModelForm): # Cable attributes status = CSVChoiceField( - choices=CONNECTION_STATUS_CHOICES, + choices=CableStatusChoices, required=False, help_text='Connection status' ) @@ -3523,7 +3523,7 @@ class CableBulkEditForm(BootstrapMixin, BulkEditForm): widget=StaticSelect2() ) status = forms.ChoiceField( - choices=add_blank_choice(CONNECTION_STATUS_CHOICES), + choices=add_blank_choice(CableStatusChoices), required=False, widget=StaticSelect2(), initial='' @@ -3597,7 +3597,7 @@ class CableFilterForm(BootstrapMixin, forms.Form): ) status = forms.ChoiceField( required=False, - choices=add_blank_choice(CONNECTION_STATUS_CHOICES), + choices=add_blank_choice(CableStatusChoices), widget=StaticSelect2() ) color = forms.CharField( diff --git a/netbox/dcim/migrations/0083_3569_cable_fields.py b/netbox/dcim/migrations/0083_3569_cable_fields.py index d6f013b37..37e616514 100644 --- a/netbox/dcim/migrations/0083_3569_cable_fields.py +++ b/netbox/dcim/migrations/0083_3569_cable_fields.py @@ -23,6 +23,11 @@ CABLE_TYPE_CHOICES = ( (5000, 'power'), ) +CABLE_STATUS_CHOICES = ( + (True, 'connected'), + (False, 'planned'), +) + CABLE_LENGTH_UNIT_CHOICES = ( (1200, 'm'), (1100, 'cm'), @@ -37,6 +42,12 @@ def cable_type_to_slug(apps, schema_editor): Cable.objects.filter(type=id).update(type=slug) +def cable_status_to_slug(apps, schema_editor): + Cable = apps.get_model('dcim', 'Cable') + for bool, slug in CABLE_STATUS_CHOICES: + Cable.objects.filter(status=str(bool)).update(status=slug) + + def cable_length_unit_to_slug(apps, schema_editor): Cable = apps.get_model('dcim', 'Cable') for id, slug in CABLE_LENGTH_UNIT_CHOICES: @@ -67,6 +78,16 @@ class Migration(migrations.Migration): field=models.CharField(blank=True, max_length=50), ), + # Cable.status + migrations.AlterField( + model_name='cable', + name='status', + field=models.CharField(max_length=50), + ), + migrations.RunPython( + code=cable_status_to_slug + ), + # Cable.length_unit migrations.AlterField( model_name='cable', diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 560ab4e1f..b9bdb026a 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -2840,9 +2840,10 @@ class Cable(ChangeLoggedModel): choices=CableTypeChoices, blank=True ) - status = models.BooleanField( - choices=CONNECTION_STATUS_CHOICES, - default=CONNECTION_STATUS_CONNECTED + status = models.CharField( + max_length=50, + choices=CableStatusChoices, + default=CableStatusChoices.STATUS_CONNECTED ) label = models.CharField( max_length=100, @@ -3039,12 +3040,12 @@ class Cable(ChangeLoggedModel): b_path = self.termination_a.trace() # Determine overall path status (connected or planned) - if self.status == CONNECTION_STATUS_PLANNED: + if self.status == CableStatusChoices.STATUS_PLANNED: path_status = CONNECTION_STATUS_PLANNED else: path_status = CONNECTION_STATUS_CONNECTED for segment in a_path[1:] + b_path[1:]: - if segment[1] is None or segment[1].status == CONNECTION_STATUS_PLANNED: + if segment[1] is None or segment[1].status == CableStatusChoices.STATUS_PLANNED: path_status = CONNECTION_STATUS_PLANNED break diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 525d8860f..3f336a3e6 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -2895,7 +2895,7 @@ class CableTest(APITestCase): 'termination_a_id': interface_a.pk, 'termination_b_type': 'dcim.interface', 'termination_b_id': interface_b.pk, - 'status': CONNECTION_STATUS_PLANNED, + 'status': CableStatusChoices.STATUS_PLANNED, 'label': 'Test Cable 4', } @@ -2949,7 +2949,7 @@ class CableTest(APITestCase): data = { 'label': 'Test Cable X', - 'status': CONNECTION_STATUS_CONNECTED, + 'status': CableStatusChoices.STATUS_CONNECTED, } url = reverse('dcim-api:cable-detail', kwargs={'pk': self.cable1.pk}) diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index ed5731695..6af5fc6d6 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -487,7 +487,11 @@ class CablePathTestCase(TestCase): self.assertIsNone(interface1.connection_status) # Third segment - cable3 = Cable(termination_a=self.front_port2, termination_b=self.interface2, status=CONNECTION_STATUS_PLANNED) + cable3 = Cable( + termination_a=self.front_port2, + termination_b=self.interface2, + status=CableStatusChoices.STATUS_PLANNED + ) cable3.save() interface1 = Interface.objects.get(pk=self.interface1.pk) self.assertEqual(interface1.connected_endpoint, self.interface2)