Просмотр исходного кода

fix(dcim): Nullify empty cable_end values instead of empty strings

Replace empty strings with null values for cable_end fields across
cable termination models. Adds data migrations to clean up inconsistent
values from earlier versions that wrote empty strings when cables were
deleted.

Fixes #22768
Martin Hauser 4 дней назад
Родитель
Сommit
91b51f62d6

+ 23 - 0
netbox/circuits/migrations/0059_nullify_empty_cable_end.py

@@ -0,0 +1,23 @@
+from django.db import migrations
+
+
+def nullify_empty_cable_end(apps, schema_editor):
+    """
+    Replace empty strings with null values on cached cable end data. Earlier versions
+    wrote an empty string when a cable termination was deleted, leaving disconnected
+    terminations inconsistent with those which have never been cabled.
+    """
+    CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
+    db_alias = schema_editor.connection.alias
+
+    CircuitTermination.objects.using(db_alias).filter(cable_end='').update(cable_end=None)
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('circuits', '0058_clear_stale_cable_profile_data'),
+    ]
+
+    operations = [
+        migrations.RunPython(nullify_empty_cable_end, migrations.RunPython.noop),
+    ]

+ 35 - 0
netbox/dcim/migrations/0241_nullify_empty_cable_end.py

@@ -0,0 +1,35 @@
+from django.db import migrations
+
+CABLED_MODELS = (
+    'ConsolePort',
+    'ConsoleServerPort',
+    'FrontPort',
+    'Interface',
+    'PowerFeed',
+    'PowerOutlet',
+    'PowerPort',
+    'RearPort',
+)
+
+
+def nullify_empty_cable_end(apps, schema_editor):
+    """
+    Replace empty strings with null values on cached cable end data. Earlier versions
+    wrote an empty string when a cable termination was deleted, leaving disconnected
+    endpoints inconsistent with those which have never been cabled.
+    """
+    db_alias = schema_editor.connection.alias
+
+    for model_name in CABLED_MODELS:
+        model = apps.get_model('dcim', model_name)
+        model.objects.using(db_alias).filter(cable_end='').update(cable_end=None)
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('dcim', '0240_clear_stale_cable_profile_data'),
+    ]
+
+    operations = [
+        migrations.RunPython(nullify_empty_cable_end, migrations.RunPython.noop),
+    ]

+ 1 - 1
netbox/dcim/signals.py

@@ -205,7 +205,7 @@ def nullify_connected_endpoints(instance, **kwargs):
     model = instance.termination_type.model_class()
     model = instance.termination_type.model_class()
     model.objects.filter(pk=instance.termination_id).update(
     model.objects.filter(pk=instance.termination_id).update(
         cable=None,
         cable=None,
-        cable_end='',
+        cable_end=None,
         cable_connector=None,
         cable_connector=None,
         cable_positions=None,
         cable_positions=None,
     )
     )

+ 2 - 2
netbox/dcim/tests/test_cablepaths.py

@@ -60,11 +60,11 @@ class LegacyCablePathTestCase(BaseCablePathTestCase):
         interface2.refresh_from_db()
         interface2.refresh_from_db()
 
 
         self.assertIsNone(interface1.cable_id)
         self.assertIsNone(interface1.cable_id)
-        self.assertEqual(interface1.cable_end, '')
+        self.assertIsNone(interface1.cable_end)
         self.assertPathIsNotSet(interface1)
         self.assertPathIsNotSet(interface1)
 
 
         self.assertIsNone(interface2.cable_id)
         self.assertIsNone(interface2.cable_id)
-        self.assertEqual(interface2.cable_end, '')
+        self.assertIsNone(interface2.cable_end)
         self.assertPathIsNotSet(interface2)
         self.assertPathIsNotSet(interface2)
 
 
     def test_102_consoleport_to_consoleserverport(self):
     def test_102_consoleport_to_consoleserverport(self):

+ 4 - 4
netbox/dcim/tests/test_signals.py

@@ -272,8 +272,8 @@ class CableSignalTestCase(TestCase):
         self.assertIsNone(interface_b._path_id)
         self.assertIsNone(interface_b._path_id)
         self.assertIsNone(interface_a.cable_id)
         self.assertIsNone(interface_a.cable_id)
         self.assertIsNone(interface_b.cable_id)
         self.assertIsNone(interface_b.cable_id)
-        self.assertEqual(interface_a.cable_end, '')
-        self.assertEqual(interface_b.cable_end, '')
+        self.assertIsNone(interface_a.cable_end)
+        self.assertIsNone(interface_b.cable_end)
 
 
     def test_deleting_profiled_cable_nullifies_endpoints(self):
     def test_deleting_profiled_cable_nullifies_endpoints(self):
         """
         """
@@ -301,7 +301,7 @@ class CableSignalTestCase(TestCase):
         for interface in (interface_a, interface_b):
         for interface in (interface_a, interface_b):
             interface.refresh_from_db()
             interface.refresh_from_db()
             self.assertIsNone(interface.cable_id)
             self.assertIsNone(interface.cable_id)
-            self.assertEqual(interface.cable_end, '')
+            self.assertIsNone(interface.cable_end)
             self.assertIsNone(interface.cable_connector)
             self.assertIsNone(interface.cable_connector)
             self.assertIsNone(interface.cable_positions)
             self.assertIsNone(interface.cable_positions)
 
 
@@ -367,7 +367,7 @@ class CableSignalTestCase(TestCase):
         termination.delete()
         termination.delete()
         interface_a.refresh_from_db()
         interface_a.refresh_from_db()
         self.assertIsNone(interface_a.cable_id)
         self.assertIsNone(interface_a.cable_id)
-        self.assertEqual(interface_a.cable_end, '')
+        self.assertIsNone(interface_a.cable_end)
 
 
 
 
 class MACAddressInterfaceSignalTestCase(TestCase):
 class MACAddressInterfaceSignalTestCase(TestCase):