Skip to content

Commit 2b792b7

Browse files
committed
fix(gui): flush ORM objects before raw rule_elements INSERT
Duplicating a device (copy-paste between subfolders) silently failed because raw rule_elements INSERT statements ran before the ORM-managed rule and rule_set objects were flushed to the DB, causing FK constraint violations that PySide6 swallowed. Restructure _duplicate_device_children to collect source rule_element rows first, explicitly flush() all ORM objects, then insert the raw rule_elements rows.
1 parent e136efe commit 2b792b7

1 file changed

Lines changed: 23 additions & 11 deletions

File tree

src/firewallfabrik/gui/object_tree_ops.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,11 @@ def _duplicate_device_children(self, session, source_device, new_device, id_map)
432432
new_addr.group_id = None
433433
session.add(new_addr)
434434

435-
# Rule sets + their rules + rule_elements.
435+
# Collect source rule_element rows and clone rule sets + rules.
436+
# We must read source rows before flushing the clones (the flush
437+
# would otherwise trigger lazy loads that might expire the source
438+
# relationships).
439+
rule_element_tasks = []
436440
for rs in source_device.rule_sets:
437441
new_rs = self._clone_object(rs, id_map)
438442
new_rs.device_id = new_device.id
@@ -441,22 +445,30 @@ def _duplicate_device_children(self, session, source_device, new_device, id_map)
441445
new_rule = self._clone_object(rule, id_map)
442446
new_rule.rule_set_id = new_rs.id
443447
session.add(new_rule)
444-
# Copy rule_elements, remapping target_id for internal refs.
445448
rows = session.execute(
446449
sqlalchemy.select(rule_elements).where(
447450
rule_elements.c.rule_id == rule.id
448451
)
449452
).all()
450-
for row in rows:
451-
target_id = id_map.get(row.target_id, row.target_id)
452-
session.execute(
453-
rule_elements.insert().values(
454-
rule_id=new_rule.id,
455-
slot=row.slot,
456-
target_id=target_id,
457-
position=row.position,
458-
)
453+
if rows:
454+
rule_element_tasks.append((new_rule, rows))
455+
456+
# Flush all ORM objects so that rule and rule_set rows exist in
457+
# the DB before we insert the raw rule_elements rows (which
458+
# reference them via FK).
459+
session.flush()
460+
461+
for new_rule, rows in rule_element_tasks:
462+
for row in rows:
463+
target_id = id_map.get(row.target_id, row.target_id)
464+
session.execute(
465+
rule_elements.insert().values(
466+
rule_id=new_rule.id,
467+
slot=row.slot,
468+
target_id=target_id,
469+
position=row.position,
459470
)
471+
)
460472

461473
@staticmethod
462474
def _duplicate_group_members(session, source_group, new_group):

0 commit comments

Comments
 (0)