From 349e803bd12bd849572033a4d4987c004a304165 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 18 Nov 2016 14:49:27 +0530 Subject: [PATCH] [fix] fetch values server side for link fields --- frappe/desk/doctype/todo/test_todo.py | 6 +++- frappe/desk/doctype/todo/todo.json | 47 ++++++++++++++++++++++++++- frappe/model/base_document.py | 35 +++++++++++++++++--- frappe/model/meta.py | 16 +++++++++ 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/frappe/desk/doctype/todo/test_todo.py b/frappe/desk/doctype/todo/test_todo.py index 2bbe82b8d0..c9d0a855fb 100644 --- a/frappe/desk/doctype/todo/test_todo.py +++ b/frappe/desk/doctype/todo/test_todo.py @@ -9,4 +9,8 @@ import unittest # test_records = frappe.get_test_records('ToDo') class TestToDo(unittest.TestCase): - pass + def test_fetch(self): + todo = frappe.get_doc(dict(doctype='ToDo', description='test todo', + assigned_by='Administrator')).insert() + self.assertEquals(todo.assigned_by_full_name, + frappe.db.get_value('User', todo.assigned_by, 'full_name')) diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index 768393c6e8..c86d8197cf 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -23,6 +23,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "", @@ -51,6 +52,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Status", @@ -80,6 +82,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Priority", @@ -110,6 +113,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "length": 0, @@ -136,6 +140,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Due Date", @@ -165,6 +170,7 @@ "ignore_user_permissions": 1, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Allocated To", @@ -193,6 +199,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "", @@ -221,6 +228,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Description", @@ -252,6 +260,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Reference", @@ -279,6 +288,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Reference Type", @@ -309,6 +319,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Reference Name", @@ -339,6 +350,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "length": 0, @@ -365,6 +377,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Role", @@ -395,6 +408,7 @@ "ignore_user_permissions": 1, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Assigned By", @@ -412,6 +426,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "assigned_by_full_name", + "fieldtype": "Read Only", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Assigned By Full Name", + "length": 0, + "no_copy": 0, + "options": "assigned_by.full_name", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -423,6 +467,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Sender", @@ -452,7 +497,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-07 05:29:56.682890", + "modified": "2016-11-18 14:45:27.214657", "modified_by": "Administrator", "module": "Desk", "name": "ToDo", diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 4121a9ec46..d091d2c636 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -426,6 +426,7 @@ class BaseDocument(object): return missing def get_invalid_links(self, is_submittable=False): + '''Returns list of invalid links and also updates fetch values if not set''' def get_msg(df, docname): if self.parentfield: return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname) @@ -434,6 +435,7 @@ class BaseDocument(object): invalid_links = [] cancelled_links = [] + for df in (self.meta.get_link_fields() + self.meta.get("fields", {"fieldtype":"Dynamic Link"})): docname = self.get(df.fieldname) @@ -449,15 +451,38 @@ class BaseDocument(object): frappe.throw(_("{0} must be set first").format(self.meta.get_label(df.options))) # MySQL is case insensitive. Preserve case of the original docname in the Link Field. - value = frappe.db.get_value(doctype, docname, "name", cache=True) - if frappe.get_meta(doctype).issingle: - value = doctype - setattr(self, df.fieldname, value) + # get a map of values ot fetch along with this link query + # that are mapped as link_fieldname.source_fieldname in Options of + # Readonly or Data or Text type fields + fields_to_fetch = [ + _df for _df in self.meta.get_fields_to_fetch(df.fieldname) + if not self.get(_df.fieldname) + ] + + if not fields_to_fetch: + # cache a single value type + values = frappe._dict(name=frappe.db.get_value(doctype, docname, + 'name', cache=True)) + else: + values_to_fetch = ['name'] + [_df.options.split('.')[-1] + for _df in fields_to_fetch] + + # don't cache if fetching other values too + values = frappe.db.get_value(doctype, docname, + values_to_fetch, as_dict=True) + + if frappe.get_meta(doctype).issingle: + values.name = doctype + + setattr(self, df.fieldname, values.name) + + for _df in fields_to_fetch: + setattr(self, _df.fieldname, values[_df.options.split('.')[-1]]) notify_link_count(doctype, docname) - if not value: + if not values.name: invalid_links.append((df.fieldname, docname, get_msg(df, docname))) elif (df.fieldname != "amended_from" diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 3435126147..f0ec98370b 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -155,6 +155,22 @@ class Meta(Document): return search_fields + def get_fields_to_fetch(self, link_fieldname): + '''Returns a list of docfield objects for fields whose values + are to be fetched and updated for a particular link field + + These fields are of type Data, Link, Text, Readonly and their + options property is set as `link_fieldname`.`source_fieldname`''' + + out = [] + for df in self.fields: + if df.fieldtype in ('Data', 'Read Only', 'Text', 'Small Text', + 'Text Editor', 'Code') and df.options and \ + df.options.startswith(link_fieldname + '.'): + out.append(df) + + return out + def get_list_fields(self): list_fields = ["name"] + [d.fieldname \ for d in self.fields if (d.in_list_view and d.fieldtype in type_map)]