fix: prevent module component duplication during import

This commit is contained in:
2026-08-05 14:24:46 +02:00
parent 8107e152d2
commit d4ba5de2e7
8 changed files with 119 additions and 7 deletions
+70 -1
View File
@@ -5,7 +5,11 @@ import pytest
from django.db import connection, models
from netbox_export.services import importer as importer_module
from netbox_export.services.importer import _field_kwargs, _find_existing
from netbox_export.services.importer import (
_field_kwargs,
_find_existing,
_identity_lookup,
)
from netbox_export.services.plugin_compat import PluginCompatibility
from netbox_export.services.references import ReferenceResolver
@@ -73,6 +77,27 @@ class RearPort(models.Model):
]
class Module(models.Model):
class Meta:
app_label = "dcim"
class ModuleBay(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
module = models.ForeignKey(Module, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
class Meta:
app_label = "dcim"
class ModularRearPort(models.Model):
module = models.ForeignKey(Module, on_delete=models.SET_NULL, null=True)
class Meta:
app_label = "compat_tests"
def tenant_record():
return {
"fields": {},
@@ -132,6 +157,50 @@ def test_unresolved_relation_in_check_constraint_blocks_initial_save():
assert missing_required == []
def test_modular_component_waits_for_module_before_initial_save():
resolver = ReferenceResolver({}, [], lambda label: None)
record = {
"fields": {},
"relations": {"module": {"ref": "dcim.module:12"}},
}
kwargs, unresolved, unresolved_values, missing_required = _field_kwargs(
ModularRearPort,
record,
resolver,
tenant_required=False,
)
assert kwargs is None
assert unresolved == []
assert unresolved_values == []
assert missing_required == []
def test_module_bay_identity_includes_owning_module():
device = Device(pk=210)
module = Module(pk=12)
resolver = ReferenceResolver(
{
"dcim.device:210": device,
"dcim.module:12": module,
},
[],
lambda label: None,
)
record = {
"fields": {"name": "3-LC"},
"relations": {
"device": {"ref": "dcim.device:210"},
"module": {"ref": "dcim.module:12"},
},
}
lookup = _identity_lookup(ModuleBay, record, resolver)
assert lookup == {"device": device, "module": module, "name": "3-LC"}
def test_resolved_nullable_unique_relation_is_deferred():
address = UniqueAddress(pk=50)
resolver = ReferenceResolver(
+22
View File
@@ -7,6 +7,7 @@ from netbox_export.services.plugin_compat import (
MISSING_TENANT_MESSAGE,
PluginCompatibility,
check_constraint_field_names,
is_module_relation,
is_tenant_relation,
relation_required_before_save,
relation_should_be_deferred,
@@ -86,6 +87,27 @@ def test_identifies_tenant_relation():
assert is_tenant_relation(relation_field("site", "dcim.site")) is False
def test_identifies_module_component_relation():
assert is_module_relation(relation_field("module", "dcim.module")) is True
assert is_module_relation(relation_field("device", "dcim.device")) is False
def test_new_module_disables_automatic_component_replication():
module = SimpleNamespace(_meta=SimpleNamespace(label_lower="dcim.module"))
PluginCompatibility.prepare_initial_save(module, is_new=True)
assert module._disable_replication is True
def test_existing_module_keeps_normal_save_behavior():
module = SimpleNamespace(_meta=SimpleNamespace(label_lower="dcim.module"))
PluginCompatibility.prepare_initial_save(module, is_new=False)
assert not hasattr(module, "_disable_replication")
def test_identifies_relations_used_by_check_constraint():
constraint = models.CheckConstraint(
condition=(