Compare commits

..

8 Commits

Author SHA1 Message Date
Arthur
959d36cafc cleanup 2026-04-14 16:34:56 -07:00
Arthur
fb417912a5 update to use post_save signal 2026-04-14 16:10:18 -07:00
Arthur
8e4588bbca update to use post_save signal 2026-04-14 16:09:53 -07:00
Arthur
58eb42a424 update to use post_save signal 2026-04-14 15:56:30 -07:00
Arthur
7b9ab87e38 cleanup 2026-04-10 12:04:48 -07:00
Arthur
90255a268f cleanup 2026-04-10 11:57:49 -07:00
Arthur
8418809344 #21879 - Add post_raw_create signal hook 2026-04-10 11:31:56 -07:00
Arthur
b39e1c73c1 #21879 - Add post_raw_create signal hook 2026-04-10 11:28:10 -07:00
6 changed files with 17 additions and 34 deletions

View File

@@ -56,9 +56,9 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check Python linting & PEP8 compliance
uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
uses: astral-sh/ruff-action@4919ec5cf1f49eff0871dbcea0da843445b837e6 # v3.6.1
with:
version: "0.15.10"
version: "0.15.2"
args: "check --output-format=github"
src: "netbox/"

View File

@@ -20,7 +20,7 @@ jobs:
steps:
- name: Create app token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: 1076524
@@ -48,7 +48,7 @@ jobs:
run: python netbox/manage.py makemessages -l ${{ env.LOCALE }}
- name: Commit changes
uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10.0.0
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
with:
add: 'netbox/translations/'
default_author: github_actions

View File

@@ -166,6 +166,19 @@ def retrace_cable_paths(instance, **kwargs):
cablepath.retrace()
@receiver(post_save, sender=Cable)
def retrace_cable_paths_on_raw_create(sender, instance, post_raw_create=False, **kwargs):
"""
Trigger cable path retracing for a Cable instance when dependencies need to be
recalculated. Callers should fire post_save with post_raw_create=True after
all CableTerminations are in place.
"""
if not post_raw_create:
return
instance._terminations_modified = True
trace_paths.send(Cable, instance=instance, created=True)
@receiver((post_delete, post_save), sender=PortMapping)
def update_passthrough_port_paths(instance, **kwargs):
"""

View File

@@ -254,7 +254,6 @@ class APIViewTestCases:
action=ObjectChangeActionChoices.ACTION_CREATE,
)
self.assertEqual(objectchange.message, data['changelog_message'])
self.assertObjectChangeData(objectchange, prechange_is_none=True, postchange_is_none=False)
def test_bulk_create_objects(self):
"""
@@ -308,7 +307,6 @@ class APIViewTestCases:
self.assertEqual(len(objectchanges), len(self.create_data))
for oc in objectchanges:
self.assertEqual(oc.message, changelog_message)
self.assertObjectChangeData(oc, prechange_is_none=True, postchange_is_none=False)
class UpdateObjectViewTestCase(APITestCase):
update_data = {}
@@ -368,8 +366,6 @@ class APIViewTestCases:
)
self.assertEqual(objectchange.action, ObjectChangeActionChoices.ACTION_UPDATE)
self.assertEqual(objectchange.message, data['changelog_message'])
self.assertObjectChangeData(objectchange, prechange_is_none=False, postchange_is_none=False)
self.assertNotEqual(objectchange.prechange_data, objectchange.postchange_data)
def test_bulk_update_objects(self):
"""
@@ -420,8 +416,6 @@ class APIViewTestCases:
for oc in objectchanges:
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_UPDATE)
self.assertEqual(oc.message, changelog_message)
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=False)
self.assertNotEqual(oc.prechange_data, oc.postchange_data)
class DeleteObjectViewTestCase(APITestCase):
@@ -470,7 +464,6 @@ class APIViewTestCases:
)
self.assertEqual(objectchange.action, ObjectChangeActionChoices.ACTION_DELETE)
self.assertEqual(objectchange.message, data['changelog_message'])
self.assertObjectChangeData(objectchange, prechange_is_none=False, postchange_is_none=True)
def test_bulk_delete_objects(self):
"""
@@ -512,7 +505,6 @@ class APIViewTestCases:
for oc in objectchanges:
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_DELETE)
self.assertEqual(oc.message, changelog_message)
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=True)
class GraphQLTestCase(APITestCase):

View File

@@ -83,20 +83,6 @@ class TestCase(_TestCase):
# Custom assertions
#
def assertObjectChangeData(self, objectchange, *, prechange_is_none: bool, postchange_is_none: bool):
"""
Assert that an ObjectChange record has the expected prechange_data and postchange_data.
Set prechange_is_none=True to assert the field is null, False to assert it is populated.
"""
if prechange_is_none:
self.assertIsNone(objectchange.prechange_data, "Expected prechange_data to be None")
else:
self.assertIsNotNone(objectchange.prechange_data, "Expected prechange_data to be populated")
if postchange_is_none:
self.assertIsNone(objectchange.postchange_data, "Expected postchange_data to be None")
else:
self.assertIsNotNone(objectchange.postchange_data, "Expected postchange_data to be populated")
def assertHttpStatus(self, response, expected_status):
"""
TestCase method. Provide more detail in the event of an unexpected HTTP response.

View File

@@ -194,7 +194,6 @@ class ViewTestCases:
self.assertEqual(len(objectchanges), 1)
self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_CREATE)
self.assertEqual(objectchanges[0].message, self.form_data['changelog_message'])
self.assertObjectChangeData(objectchanges[0], prechange_is_none=True, postchange_is_none=False)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
def test_create_object_with_constrained_permission(self):
@@ -302,8 +301,6 @@ class ViewTestCases:
self.assertEqual(len(objectchanges), 1)
self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_UPDATE)
self.assertEqual(objectchanges[0].message, self.form_data['changelog_message'])
self.assertObjectChangeData(objectchanges[0], prechange_is_none=False, postchange_is_none=False)
self.assertNotEqual(objectchanges[0].prechange_data, objectchanges[0].postchange_data)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
def test_edit_object_with_constrained_permission(self):
@@ -399,7 +396,6 @@ class ViewTestCases:
self.assertEqual(len(objectchanges), 1)
self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_DELETE)
self.assertEqual(objectchanges[0].message, form_data['changelog_message'])
self.assertObjectChangeData(objectchanges[0], prechange_is_none=False, postchange_is_none=True)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_delete_object_with_constrained_permission(self):
@@ -721,7 +717,6 @@ class ViewTestCases:
for oc in objectchanges:
self.assertEqual(oc.message, data['changelog_message'])
self.assertObjectChangeData(oc, prechange_is_none=True, postchange_is_none=False)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_bulk_update_objects_with_permission(self):
@@ -875,8 +870,6 @@ class ViewTestCases:
for oc in objectchanges:
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_UPDATE)
self.assertEqual(oc.message, data['changelog_message'])
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=False)
self.assertNotEqual(oc.prechange_data, oc.postchange_data)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
def test_bulk_edit_objects_with_constrained_permission(self):
@@ -973,7 +966,6 @@ class ViewTestCases:
for oc in objectchanges:
self.assertEqual(oc.action, ObjectChangeActionChoices.ACTION_DELETE)
self.assertEqual(oc.message, data['changelog_message'])
self.assertObjectChangeData(oc, prechange_is_none=False, postchange_is_none=True)
def test_bulk_delete_objects_with_constrained_permission(self):
pk_list = self._get_queryset().values_list('pk', flat=True)