From f5fed5b4be71f896208af441c6689eca37e7efa3 Mon Sep 17 00:00:00 2001 From: Sumit Bhanushali Date: Mon, 27 Oct 2025 13:00:43 +0530 Subject: [PATCH] perf: optimize expand link for table fields --- frappe/api/v1.py | 8 +++----- frappe/client.py | 42 ++---------------------------------------- frappe/utils/data.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 45 deletions(-) diff --git a/frappe/api/v1.py b/frappe/api/v1.py index 962dbc5af6..744ad89cdb 100644 --- a/frappe/api/v1.py +++ b/frappe/api/v1.py @@ -4,6 +4,7 @@ from werkzeug.routing import Rule import frappe from frappe import _ +from frappe.utils import attach_expanded_links from frappe.utils.data import sbool @@ -104,11 +105,8 @@ def get_values_for_table_and_multiselect_fields(doc_dict): table_fields = meta.get_table_fields() for field in table_fields: - if not doc_dict.get(field.fieldname): - continue - - for value in doc_dict.get(field.fieldname): - value.update(get_values_for_link_and_dynamic_link_fields(value)) + table_link_fieldnames = [f.fieldname for f in frappe.get_meta(field.options).get_link_fields()] + attach_expanded_links(field.options, doc_dict.get(field.fieldname), table_link_fieldnames) def execute_doc_method(doctype: str, name: str, method: str | None = None): diff --git a/frappe/client.py b/frappe/client.py index a2dab393bf..13755531cc 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -2,7 +2,6 @@ # License: MIT. See LICENSE import json import os -from collections import defaultdict from typing import TYPE_CHECKING import frappe @@ -12,7 +11,7 @@ from frappe import _ from frappe.desk.reportview import validate_args from frappe.model.db_query import check_parent_permission from frappe.model.utils import is_virtual_doctype -from frappe.utils import get_safe_filters +from frappe.utils import attach_expanded_links, get_safe_filters from frappe.utils.caching import http_cache if TYPE_CHECKING: @@ -74,44 +73,7 @@ def get_list( if fields and not fields[0] == "*": expand = [f for f in expand if f in fields] - meta = frappe.get_meta(doctype) - - link_fields = {f.fieldname: f for f in meta.get_link_fields() + meta.get_dynamic_link_fields()} - - doctype_values = defaultdict(set) - field_to_doctype = {} - - for fieldname in expand: - if fieldname not in link_fields: - continue - e = link_fields[fieldname] - link_doctype = e.options - field_to_doctype[fieldname] = link_doctype - - for li in _list: - val = li.get(fieldname) - if val: - doctype_values[link_doctype].add(val) - - doctype_title_maps = {} - - for link_doctype, values in doctype_values.items(): - records = frappe.get_all( - link_doctype, - filters={"name": ["in", list(values)]}, - fields=["*"], - ) - doctype_title_maps[link_doctype] = {r["name"]: r for r in records} - - for li in _list: - for fieldname in expand: - if fieldname not in field_to_doctype: - continue - link_doctype = field_to_doctype[fieldname] - val = li.get(fieldname) - val_title = doctype_title_maps.get(link_doctype, {}).get(val) - if val and val_title: - li[fieldname] = val_title + attach_expanded_links(doctype, _list, expand) return _list diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 5ff2105214..e37e4e0c5e 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -2776,3 +2776,47 @@ def map_trackers(url_trackers: dict, create: bool = False): frappe_trackers["utm_content"] = url_content return frappe_trackers + + +def attach_expanded_links(doctype: str, docs: list, fields_to_expand: list): + if not fields_to_expand: + return + + meta = frappe.get_meta(doctype) + + link_fields = {f.fieldname: f for f in meta.get_link_fields() + meta.get_dynamic_link_fields()} + + doctype_values = defaultdict(set) + field_to_doctype = {} + + for fieldname in fields_to_expand: + if fieldname not in link_fields: + continue + e = link_fields[fieldname] + link_doctype = e.options + field_to_doctype[fieldname] = link_doctype + + for li in docs: + val = li.get(fieldname) + if val: + doctype_values[link_doctype].add(val) + + doctype_title_maps = {} + + for link_doctype, values in doctype_values.items(): + records = frappe.get_all( + link_doctype, + filters={"name": ["in", list(values)]}, + fields=["*"], + ) + doctype_title_maps[link_doctype] = {r["name"]: r for r in records} + + for li in docs: + for fieldname in fields_to_expand: + if fieldname not in field_to_doctype: + continue + link_doctype = field_to_doctype[fieldname] + val = li.get(fieldname) + val_title = doctype_title_maps.get(link_doctype, {}).get(val) + if val and val_title: + li[fieldname] = val_title