From 691eb31b6159fad45d7d2d6085ce84643ede8823 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 5 Jan 2024 10:07:14 +0530 Subject: [PATCH] fix: delete existing children first to avoid `UniqueValidationError` --- frappe/model/document.py | 45 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/frappe/model/document.py b/frappe/model/document.py index 1201d3755b0..ec0799e99ff 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -420,36 +420,35 @@ def update_children(self): def update_child_table(self, fieldname: str, df: Optional["DocField"] = None): """sync child table for given fieldname""" - rows = [] df: "DocField" = df or self.meta.get_field(fieldname) + all_rows = self.get(df.fieldname) - for d in self.get(df.fieldname): - d: Document - d.db_update() - rows.append(d.name) - - if ( - df.options in (self.flags.ignore_children_type or []) + # delete rows that do not match the ones in the document + # if the doctype isn't in ignore_children_type flag and isn't virtual + if not ( + df.options in (self.flags.ignore_children_type or ()) or frappe.get_meta(df.options).is_virtual == 1 ): - # do not delete rows for this because of flags - # hack for docperm :( - return + existing_row_names = [row.name for row in all_rows if row.name and not row.is_new()] + + tbl = frappe.qb.DocType(df.options) + qry = ( + frappe.qb.from_(tbl) + .where(tbl.parent == self.name) + .where(tbl.parenttype == self.doctype) + .where(tbl.parentfield == fieldname) + .delete() + ) - # delete rows that do not match the ones in the document - tbl = frappe.qb.DocType(df.options) - qry = ( - frappe.qb.from_(tbl) - .where(tbl.parent == self.name) - .where(tbl.parenttype == self.doctype) - .where(tbl.parentfield == fieldname) - .delete() - ) + if existing_row_names: + qry = qry.where(tbl.name.notin(existing_row_names)) - if rows: - qry = qry.where(tbl.name.notin(rows)) + qry.run() - qry.run() + # update / insert + for d in all_rows: + d: Document + d.db_update() def get_doc_before_save(self) -> "Document": return getattr(self, "_doc_before_save", None)