Merge pull request #22780 from netbox-community/22768-cable-removal-stores-an-empty-string-instead-of-null-in

Fixes #22768: Store null values for cable_end when removing Cables
This commit is contained in:
bctiemann 2026-07-27 15:02:52 -04:00 committed by GitHub
commit 8aa7b3b2c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 7 deletions

View File

@ -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),
]

View File

@ -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),
]

View File

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

View File

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

View File

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