From d50fdbe3bc71d86f01dea11a924990e63b8d54e5 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sun, 16 Mar 2025 13:58:45 +0530 Subject: [PATCH] perf: much faster `doc.update` --- frappe/core/doctype/doctype/doctype.py | 4 +- frappe/model/base_document.py | 53 +++++++++++++++----------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 338a8e99c2..4a84a2d8d7 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -25,7 +25,7 @@ from frappe.model import ( no_value_fields, table_fields, ) -from frappe.model.base_document import get_controller +from frappe.model.base_document import RESERVED_KEYWORDS, get_controller from frappe.model.docfield import supports_translation from frappe.model.document import Document from frappe.model.meta import Meta @@ -1261,7 +1261,7 @@ def validate_fields(meta: Meta): validate_column_name(fieldname) def check_invalid_fieldnames(docname, fieldname): - if fieldname in Document._reserved_keywords: + if fieldname in RESERVED_KEYWORDS: frappe.throw( _("{0}: fieldname cannot be set to reserved keyword {1}").format( frappe.bold(docname), diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 311a5a3ded..c630dfa29d 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -129,23 +129,23 @@ def import_controller(doctype): return class_ -class BaseDocument: - _reserved_keywords = frozenset( - ( - "doctype", - "meta", - "flags", - "_weakref", - "_parent_doc", - "_table_fields", - "_doc_before_save", - "_table_fieldnames", - "_reserved_keywords", - "permitted_fieldnames", - "dont_update_if_missing", - ) +RESERVED_KEYWORDS = frozenset( + ( + "doctype", + "meta", + "flags", + "_weakref", + "_parent_doc", + "_table_fields", + "_doc_before_save", + "_table_fieldnames", + "permitted_fieldnames", + "dont_update_if_missing", ) +) + +class BaseDocument: def __init__(self, d): if d.get("doctype"): self.doctype = d["doctype"] @@ -199,15 +199,24 @@ class BaseDocument: "user": "admin", "balance": 42000 }) + + Developer Note: Logic in the set method is re-implemented here for perf """ - # set name first, as it is used a reference in child document - if "name" in d: - self.name = d["name"] + self.__dict__.update(key_val for key_val in d.items() if key_val[0] not in RESERVED_KEYWORDS) + if not self._table_fieldnames or self.flags.get("ignore_children", False): + return self - as_value = not self._table_fieldnames or self.flags.get("ignore_children", False) - for key, value in d.items(): - self.set(key, value, as_value=as_value) + __dict = self.__dict__ + for key in self._table_fieldnames: + if key not in __dict: + continue + + value = __dict[key] + __dict[key] = [] + + if value: + self.extend(key, value) return self @@ -251,7 +260,7 @@ class BaseDocument: return self.get(key, filters=filters, limit=1)[0] def set(self, key, value, as_value=False): - if key in self._reserved_keywords: + if key in RESERVED_KEYWORDS: return if not as_value and key in self._table_fieldnames: