Skip to content

Commit 68ad840

Browse files
committed
When using SKIP_CHILDREN_ON_DELETE flag, still remove child objects from the base DSync.
1 parent 59c3c43 commit 68ad840

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

dsync/__init__.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -501,16 +501,17 @@ def _sync_from_diff_element(
501501
return
502502

503503
if element.action == "create":
504-
self.add(obj)
505504
if parent_model:
506505
parent_model.add_child(obj)
506+
self.add(obj)
507507
elif element.action == "delete":
508-
self.remove(obj)
509508
if parent_model:
510509
parent_model.remove_child(obj)
511-
512-
if element.action == "delete" and obj.model_flags & DSyncModelFlags.SKIP_CHILDREN_ON_DELETE:
513-
return
510+
if obj.model_flags & DSyncModelFlags.SKIP_CHILDREN_ON_DELETE:
511+
# We don't need to process the child objects, but we do need to discard them
512+
self.remove(obj, remove_children=True)
513+
return
514+
self.remove(obj)
514515

515516
for child in element.get_children():
516517
self._sync_from_diff_element(child, flags=flags, parent_model=obj, logger=logger)
@@ -622,11 +623,12 @@ def add(self, obj: DSyncModel):
622623

623624
self._data[modelname][uid] = obj
624625

625-
def remove(self, obj: DSyncModel):
626+
def remove(self, obj: DSyncModel, remove_children: bool = False):
626627
"""Remove a DSyncModel object from the store.
627628
628629
Args:
629-
obj (DSyncModel): object to delete
630+
obj (DSyncModel): object to remove
631+
remove_children (bool): If True, also recursively remove any children of this object
630632
631633
Raises:
632634
ObjectNotFound: if the object is not present
@@ -642,6 +644,13 @@ def remove(self, obj: DSyncModel):
642644

643645
del self._data[modelname][uid]
644646

647+
if remove_children:
648+
for child_type, child_fieldname in obj.get_children_mapping().items():
649+
for child_id in getattr(obj, child_fieldname):
650+
child_obj = self.get(child_type, child_id)
651+
if child_obj:
652+
self.remove(child_obj, remove_children=remove_children)
653+
645654

646655
# DSyncModel references DSync and DSync references DSyncModel. Break the typing loop:
647656
DSyncModel.update_forward_refs()

tests/unit/test_dsync.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,13 @@ class NoDeleteInterfaceDSync(BackendA):
332332
extra_interface = extra_models.interface(name="eth0", device_name="nyc-spine3")
333333
extra_device.add_child(extra_interface)
334334
extra_models.add(extra_interface)
335+
assert extra_models.get(extra_models.interface, "nyc-spine3__eth0") is not None
335336

336337
# NoDeleteInterface.delete() should not be called since we're deleting its parent only
337338
extra_models.sync_from(backend_a)
339+
# The extra interface should have been removed from the DSync without calling its delete() method
340+
assert extra_models.get(extra_models.interface, extra_interface.get_unique_id()) is None
341+
# The sync should be complete, regardless
342+
diff = extra_models.diff_from(backend_a)
343+
diff.print_detailed()
344+
assert not diff.has_diffs()

0 commit comments

Comments
 (0)