fix: reconcile component mappings by natural key

This commit is contained in:
2026-08-05 14:05:40 +02:00
parent f22276d68d
commit 8107e152d2
6 changed files with 83 additions and 9 deletions
+65 -1
View File
@@ -1,9 +1,11 @@
import uuid
from typing import ClassVar
import pytest
from django.db import connection, models
from netbox_export.services.importer import _field_kwargs
from netbox_export.services import importer as importer_module
from netbox_export.services.importer import _field_kwargs, _find_existing
from netbox_export.services.plugin_compat import PluginCompatibility
from netbox_export.services.references import ReferenceResolver
@@ -52,6 +54,25 @@ class UniqueAddressOwner(models.Model):
app_label = "compat_tests"
class Device(models.Model):
class Meta:
app_label = "dcim"
class RearPort(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
class Meta:
app_label = "dcim"
constraints: ClassVar[list] = [
models.UniqueConstraint(
fields=("device", "name"),
name="dcim_rearport_unique_device_name",
)
]
def tenant_record():
return {
"fields": {},
@@ -164,3 +185,46 @@ def test_existing_unique_relation_is_released_before_reassignment():
with connection.schema_editor() as schema_editor:
schema_editor.delete_model(UniqueAddressOwner)
schema_editor.delete_model(UniqueAddress)
def test_natural_key_rebinds_stale_mapping_instead_of_creating_duplicate(monkeypatch):
with connection.schema_editor() as schema_editor:
schema_editor.create_model(Device)
schema_editor.create_model(RearPort)
try:
device = Device.objects.create()
natural = RearPort.objects.create(device=device, name="3-LC")
stale_mapping_target = RearPort(pk=999, device=device, name="Alt")
monkeypatch.setattr(
importer_module,
"_mapped_object",
lambda source_instance, record, model: stale_mapping_target,
)
warnings = []
resolver = ReferenceResolver(
{"dcim.device:160": device},
warnings,
lambda label: None,
)
record = {
"id": "dcim.rearport:77",
"model": "dcim.rearport",
"source_pk": "77",
"fields": {"name": "3-LC"},
"relations": {"device": {"ref": "dcim.device:160"}},
}
existing = _find_existing(uuid.uuid4(), RearPort, record, resolver)
assert existing == natural
assert warnings == [
(
"Gespeicherte Zuordnung für dcim.rearport:77 wurde von Ziel-ID 999 auf "
f"Ziel-ID {natural.pk} korrigiert, da der Fachschlüssel "
f"{{'device': <Device: Device object ({device.pk})>, 'name': '3-LC'}} bereits existiert."
)
]
finally:
with connection.schema_editor() as schema_editor:
schema_editor.delete_model(RearPort)
schema_editor.delete_model(Device)