From eb8e683c267f826808fab6645acff73833daa4d9 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Tue, 21 Apr 2026 18:20:16 +0530 Subject: [PATCH] feat: add delete_custom_fields function to remove custom fields from doctypes --- .../doctype/custom_field/custom_field.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index 41e86656e9..d72d26603f 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -443,3 +443,64 @@ def _update_fieldname_references(field: CustomField, old_fieldname: str, new_fie "insert_after", new_fieldname, ) + + +def delete_custom_fields(custom_fields: dict): + """ + Delete custom fields from the given doctypes. + + :param custom_fields: Dictionary of doctypes with fields to be deleted. + + --- + Structure of the `custom_fields` dictionary: + + ```py + # first structure + { + "DocType1": ["field1", "field2", ...], + "DocType2": ["field1", "field2", ...], + ... + } + + # second structure + { + "DocType1": [ + {"fieldname": "field1", ...}, + {"fieldname": "field2", ...}, + ... + ], + "DocType2": [ + {"fieldname": "field1", ...}, + {"fieldname": "field2", ...}, + ... + ], + ... + } + ``` + + """ + for doctype, fields in custom_fields.items(): + fieldnames = [] + + if isinstance(fields, (list, tuple, set)): + for field in fields: + if isinstance(field, str): + fieldnames.append(field) + elif isinstance(field, dict) and field.get("fieldname"): + fieldnames.append(field["fieldname"]) + + # avoid redundant values in SQL IN clause + fieldnames = list(set(fieldnames)) + + if not fieldnames: + continue + + frappe.db.delete( + "Custom Field", + { + "fieldname": ("in", fieldnames), + "dt": doctype, + }, + ) + + frappe.clear_cache(doctype=doctype)