From d9fc9f21f933ee4e4433a4df352df58e19a8064c Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Sat, 21 Jun 2025 20:12:39 +0530 Subject: [PATCH] perf: faster meta serialisation --- frappe/model/meta.py | 51 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 8ac2018bd2..8ffca6e632 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -45,6 +45,9 @@ from frappe.utils import cached_property, cast, cint, cstr from frappe.utils.caching import site_cache from frappe.utils.data import add_to_date, get_datetime +ListOrTuple = list | tuple +SerializableTypes = str | int | float | datetime + DEFAULT_FIELD_LABELS = { "name": _lt("ID"), "creation": _lt("Created On"), @@ -176,31 +179,7 @@ class Meta(Document): self.check_if_large_table() def as_dict(self, no_nulls=False): - def serialize(doc): - if isinstance(doc, dict): - return doc.copy() - out = {} - for key, value in doc.__dict__.items(): - if isinstance(value, list | tuple): - if not value or not isinstance(value[0], BaseDocument): - # non standard list object, skip - continue - - value = [serialize(d) for d in value] - - if (not no_nulls and value is None) or isinstance( - value, str | int | float | datetime | list | tuple - ): - out[key] = value - - # set empty lists for unset table fields - for fieldname in TABLE_DOCTYPES_FOR_DOCTYPE.keys(): - if out.get(fieldname) is None: - out[fieldname] = [] - - return out - - return serialize(self) + return _serialize(self, no_nulls=no_nulls) def get_link_fields(self): return self.get("fields", {"fieldtype": "Link", "options": ["!=", "[Select]"]}) @@ -977,6 +956,28 @@ def _update_field_order_based_on_insert_after(field_order, insert_after_map): field_order.extend(fields) +def _serialize(doc, no_nulls=False, *, is_child=False): + out = {} + for key, value in doc.__dict__.items(): + if not is_child and isinstance(value, ListOrTuple): + if not value or not isinstance(value[0], BaseDocument): + # non standard list object, skip + continue + + out[key] = [_serialize(d, no_nulls=no_nulls, is_child=True) for d in value] + + elif (not no_nulls and value is None) or isinstance(value, SerializableTypes): + out[key] = value + + # set empty lists for unset table fields + if not is_child: + for fieldname in TABLE_DOCTYPES_FOR_DOCTYPE: + if out.get(fieldname) is None: + out[fieldname] = [] + + return out + + if typing.TYPE_CHECKING: # This is DX hack to add all fields from DocType to meta for autocompletions. # Meta is technically doctype + special fields on meta.