From 442100040930a0f0ea5b598320bbbab17131c65f Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 19 Aug 2026 12:40:17 +0200 Subject: [PATCH] fix: ignore reverse relations during tenant autofill --- README.md | 7 ++++++- netbox_utilities/__init__.py | 2 +- netbox_utilities/tenant_autofill.py | 18 +++++++++--------- netbox_utilities/tests/test_reorder_rack.py | 2 +- netbox_utilities/tests/test_tenant_autofill.py | 11 +++++++++++ netbox_utilities/tests/test_topology_views.py | 2 +- pyproject.toml | 2 +- 7 files changed, 30 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6772d15..554d1a0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # NetBox Utilities -Plugin für **NetBox 4.6.5 bis 4.6.7** mit zwölf Funktionen: +Plugin für **NetBox 4.6.5 bis 4.6.8** mit zwölf Funktionen: - Jeder Benutzer kann die Menüs der linken Navigation verschieben oder ausblenden. - Ein Dropdown in der Kopfleiste setzt einen sitzungsweiten Filter für einen Mandanten oder eine Mandantengruppe. @@ -17,6 +17,11 @@ Plugin für **NetBox 4.6.5 bis 4.6.7** mit zwölf Funktionen: Die Navigationseinstellungen sind benutzerbezogen. Die aktive Mandanten- oder Gruppenauswahl wird in der jeweiligen Browser-Session gespeichert. +Ab Version `0.10.1` ignoriert die automatische Mandantenermittlung +mehrwertige Reverse-Relationen. Das behebt unter NetBox 4.6.8 insbesondere den +Fehler `'RelatedManager' object has no attribute '_meta'` beim Öffnen der +Seite zum Anlegen einer VLAN-Gruppe. + ## Kompatibilität - NetBox `>=4.6.5,<4.7` diff --git a/netbox_utilities/__init__.py b/netbox_utilities/__init__.py index 3f4443e..73551f7 100644 --- a/netbox_utilities/__init__.py +++ b/netbox_utilities/__init__.py @@ -1,6 +1,6 @@ from netbox.plugins import PluginConfig, get_plugin_config -__version__ = "0.10.0" +__version__ = "0.10.1" class NetBoxUtilitiesConfig(PluginConfig): diff --git a/netbox_utilities/tenant_autofill.py b/netbox_utilities/tenant_autofill.py index a7c785d..7332bba 100644 --- a/netbox_utilities/tenant_autofill.py +++ b/netbox_utilities/tenant_autofill.py @@ -25,16 +25,20 @@ def tenant_id_from_object(obj, depth=0): if isinstance(obj, Tenant): return obj.pk - field_names = {field.name for field in obj._meta.get_fields()} - if "tenant" in field_names and getattr(obj, "tenant_id", None): + meta = getattr(obj, "_meta", None) + if meta is None or not hasattr(meta, "get_fields"): + return None + fields = {field.name: field for field in meta.get_fields()} + if "tenant" in fields and getattr(obj, "tenant_id", None): return obj.tenant_id for relation in PARENT_RELATIONS: - if relation not in field_names: + field = fields.get(relation) + if field is None or getattr(field, "one_to_many", False) or getattr(field, "many_to_many", False): continue try: parent = getattr(obj, relation, None) - except (ObjectDoesNotExist, ValueError): + except (AttributeError, ObjectDoesNotExist, ValueError): continue if tenant_id := tenant_id_from_object(parent, depth + 1): return tenant_id @@ -76,11 +80,7 @@ def _related_objects(form, field_name): def _is_cable(instance): meta = getattr(instance, "_meta", None) - return bool( - meta - and getattr(meta, "app_label", None) == "dcim" - and getattr(meta, "model_name", None) == "cable" - ) + return bool(meta and getattr(meta, "app_label", None) == "dcim" and getattr(meta, "model_name", None) == "cable") def _instance_cable_terminations(instance): diff --git a/netbox_utilities/tests/test_reorder_rack.py b/netbox_utilities/tests/test_reorder_rack.py index dfc7e96..e0b57c4 100644 --- a/netbox_utilities/tests/test_reorder_rack.py +++ b/netbox_utilities/tests/test_reorder_rack.py @@ -449,7 +449,7 @@ class ReorderRackFrontendTest(SimpleTestCase): self.assertEqual(template_name, "netbox_utilities/reorder_rack.html") self.assertEqual(context["reorder_devices"][0]["label"], "LEO-Fritzbox") self.assertEqual(context["reorder_devices"][0]["grid_width"], 6) - self.assertEqual(context["asset_version"], "0.10.0") + self.assertEqual(context["asset_version"], "0.10.1") self.assertIs(context["reorder_rack_width_data"], get_width_data.return_value) get_width_data.assert_called_once() self.assertIs(get_width_data.call_args.kwargs["rack"], rack) diff --git a/netbox_utilities/tests/test_tenant_autofill.py b/netbox_utilities/tests/test_tenant_autofill.py index c436a41..5830277 100644 --- a/netbox_utilities/tests/test_tenant_autofill.py +++ b/netbox_utilities/tests/test_tenant_autofill.py @@ -50,6 +50,17 @@ class FakeQuerySet: class TenantAutofillTest(SimpleTestCase): + def test_netbox_vlan_group_reverse_scopes_do_not_break_autofill(self): + from ipam.models import VLANGroup + + self.assertIsNone(tenant_id_from_object(VLANGroup())) + + def test_ignores_reverse_relation_managers_on_scoped_objects(self): + reverse_manager = SimpleNamespace() + vlan_group = FakeObject(site=reverse_manager, rack=reverse_manager) + + self.assertIsNone(tenant_id_from_object(vlan_group)) + def test_finds_tenant_through_parent_relation(self): rack = FakeObject(tenant_id=42) device = FakeObject(rack=rack) diff --git a/netbox_utilities/tests/test_topology_views.py b/netbox_utilities/tests/test_topology_views.py index f6f1ffd..b0eace9 100644 --- a/netbox_utilities/tests/test_topology_views.py +++ b/netbox_utilities/tests/test_topology_views.py @@ -246,7 +246,7 @@ class TopologyViewsRackWidthTest(SimpleTestCase): self.assertIn('id="netbox-utilities-topology-rack-width-styles"', html) self.assertIn('.rack-device[href="/dcim/devices/334/"]', html) self.assertIn("left: calc(50% + 3px) !important", html) - self.assertIn("netbox_utilities/topology-rack-width.js?v=0.10.0", html) + self.assertIn("netbox_utilities/topology-rack-width.js?v=0.10.1", html) self.assertIn("left:calc(0% + 3px)!important", html) self.assertIn("left:calc(50% + 3px)!important", html) self.assertIn("width:calc(50% - 6px)!important", html) diff --git a/pyproject.toml b/pyproject.toml index 19db81e..6a1c01d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "netbox-utilities" -version = "0.10.0" +version = "0.10.1" description = "Navigation, tenant utilities, connection VLANs, partial-width racks, and bulk operations for NetBox 4.6" readme = "README.md" requires-python = ">=3.12"