refactor: separate only computed CTs, with new include_computed flag
This commit is contained in:
parent
7522b79f57
commit
a084f5a2f3
10 changed files with 61 additions and 66 deletions
|
|
@ -447,7 +447,7 @@ def get_title_values_for_table_and_multiselect_fields(doc, table_fields=None):
|
|||
|
||||
if not table_fields:
|
||||
meta = frappe.get_meta(doc.doctype)
|
||||
table_fields = meta.get_table_fields(ignore_virtual=False)
|
||||
table_fields = meta.get_table_fields(include_computed=True)
|
||||
|
||||
for field in table_fields:
|
||||
if not doc.get(field.fieldname):
|
||||
|
|
|
|||
|
|
@ -138,27 +138,27 @@ def import_controller(doctype):
|
|||
raise ImportError(f"{doctype}: {classname} is not a subclass of BaseDocument")
|
||||
|
||||
class_ = _get_extended_class(class_, doctype)
|
||||
return _update_virtual_ct_props(class_, doctype)
|
||||
return _update_computed_ct_props(class_, doctype)
|
||||
|
||||
|
||||
def _update_virtual_ct_props(class_, doctype):
|
||||
if doctype in DOCTYPES_FOR_DOCTYPE or getattr(class_, "_virtual_ct_props_updated", False):
|
||||
def _update_computed_ct_props(class_, doctype):
|
||||
if doctype in DOCTYPES_FOR_DOCTYPE or getattr(class_, "_computed_ct_props_updated", False):
|
||||
return class_
|
||||
|
||||
meta = frappe.get_meta(doctype)
|
||||
for df in meta.get_table_fields(ignore_virtual=False):
|
||||
for df in meta.get_table_fields(include_computed=True):
|
||||
if df.is_virtual:
|
||||
_update_virtual_ct_prop(class_, df)
|
||||
_update_computed_ct_prop(class_, df)
|
||||
|
||||
class_._virtual_ct_props_updated = True
|
||||
class_._computed_ct_props_updated = True
|
||||
return class_
|
||||
|
||||
|
||||
def _update_virtual_ct_prop(class_, df):
|
||||
def _update_computed_ct_prop(class_, df):
|
||||
fieldname = df.fieldname
|
||||
original_prop = getattr(class_, fieldname, None)
|
||||
|
||||
def virtual_ct_prop(self):
|
||||
def computed_ct_prop(self):
|
||||
if original_prop and is_a_property(original_prop):
|
||||
value = original_prop.__get__(self, type(self))
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ def _update_virtual_ct_prop(class_, df):
|
|||
self.set(fieldname, value)
|
||||
return self.__dict__[fieldname]
|
||||
|
||||
setattr(class_, fieldname, property(virtual_ct_prop))
|
||||
setattr(class_, fieldname, property(computed_ct_prop))
|
||||
|
||||
|
||||
def _get_extended_class(base_class, doctype):
|
||||
|
|
@ -216,22 +216,6 @@ def _get_extended_class(base_class, doctype):
|
|||
)
|
||||
|
||||
|
||||
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"):
|
||||
|
|
@ -450,7 +434,7 @@ class BaseDocument:
|
|||
controller = get_controller(doctype)
|
||||
child = controller.__new__(controller)
|
||||
child._table_fieldnames = TABLE_DOCTYPES_FOR_CHILD_TABLES
|
||||
child._non_virtual_table_fieldnames = TABLE_DOCTYPES_FOR_CHILD_TABLES
|
||||
child._non_computed_table_fieldnames = TABLE_DOCTYPES_FOR_CHILD_TABLES
|
||||
child.__init__(value)
|
||||
|
||||
__dict = child.__dict__
|
||||
|
|
@ -478,13 +462,13 @@ class BaseDocument:
|
|||
return self.meta._table_doctypes
|
||||
|
||||
@cached_property
|
||||
def _non_virtual_table_fieldnames(self) -> dict:
|
||||
def _non_computed_table_fieldnames(self) -> dict:
|
||||
if self.doctype in DOCTYPES_FOR_DOCTYPE:
|
||||
return self._table_fieldnames
|
||||
|
||||
return self.meta._non_virtual_table_doctypes
|
||||
return self.meta._non_computed_table_doctypes
|
||||
|
||||
def _get_table_fields(self, ignore_virtual=True):
|
||||
def _get_table_fields(self, include_computed=False):
|
||||
"""
|
||||
To get table fields during Document init
|
||||
Meta.get_table_fields goes into recursion for special doctypes
|
||||
|
|
@ -497,7 +481,7 @@ class BaseDocument:
|
|||
if self.doctype in DOCTYPES_FOR_DOCTYPE:
|
||||
return ()
|
||||
|
||||
return self.meta.get_table_fields(ignore_virtual=ignore_virtual)
|
||||
return self.meta.get_table_fields(include_computed=include_computed)
|
||||
|
||||
def _evaluate_virtual_field_options(self, options):
|
||||
from frappe.utils.safe_exec import get_safe_globals
|
||||
|
|
@ -581,12 +565,12 @@ class BaseDocument:
|
|||
without worrying about whether or not they have values
|
||||
"""
|
||||
|
||||
if not self._non_virtual_table_fieldnames:
|
||||
if not self._non_computed_table_fieldnames:
|
||||
return
|
||||
|
||||
__dict = self.__dict__
|
||||
|
||||
for fieldname in self._non_virtual_table_fieldnames:
|
||||
for fieldname in self._non_computed_table_fieldnames:
|
||||
if __dict.get(fieldname) is None:
|
||||
__dict[fieldname] = []
|
||||
|
||||
|
|
@ -646,13 +630,13 @@ class BaseDocument:
|
|||
no_child_table_fields=False,
|
||||
no_private_properties=False,
|
||||
*,
|
||||
ignore_virtual_child_tables=False,
|
||||
ignore_computed_child_tables=False,
|
||||
) -> dict:
|
||||
doc = self.get_valid_dict(convert_dates_to_str=convert_dates_to_str, ignore_nulls=no_nulls)
|
||||
doc["doctype"] = self.doctype
|
||||
|
||||
table_fieldnames = (
|
||||
self._non_virtual_table_fieldnames if ignore_virtual_child_tables else self._table_fieldnames
|
||||
self._non_computed_table_fieldnames if ignore_computed_child_tables else self._table_fieldnames
|
||||
)
|
||||
for fieldname in table_fieldnames:
|
||||
children = getattr(self, fieldname, None) or []
|
||||
|
|
@ -811,7 +795,7 @@ class BaseDocument:
|
|||
"""Raw update parent + children
|
||||
DOES NOT VALIDATE AND CALL TRIGGERS"""
|
||||
self.db_update()
|
||||
for fieldname in self._non_virtual_table_fieldnames:
|
||||
for fieldname in self._non_computed_table_fieldnames:
|
||||
for doc in self.get(fieldname):
|
||||
doc.db_update()
|
||||
|
||||
|
|
@ -1531,9 +1515,22 @@ def _filter(data, filters, limit=None):
|
|||
return out
|
||||
|
||||
|
||||
CACHED_PROPERTIES = (prop for prop, value in vars(BaseDocument).items() if isinstance(value, cached_property))
|
||||
|
||||
UNPICKLABLE_KEYS = frozenset(
|
||||
(
|
||||
"_parent_doc",
|
||||
*(prop for prop, value in vars(BaseDocument).items() if isinstance(value, cached_property)),
|
||||
*CACHED_PROPERTIES,
|
||||
)
|
||||
)
|
||||
|
||||
RESERVED_KEYWORDS = frozenset(
|
||||
(
|
||||
"doctype",
|
||||
"flags",
|
||||
"_parent_doc",
|
||||
"_doc_before_save",
|
||||
"dont_update_if_missing",
|
||||
*CACHED_PROPERTIES,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ class Document(BaseDocument):
|
|||
for d in self.get_all_children():
|
||||
d.db_insert()
|
||||
|
||||
self.reset_virtual_child_tables()
|
||||
self.reset_computed_child_tables()
|
||||
self.run_method("after_insert")
|
||||
self.flags.in_insert = True
|
||||
|
||||
|
|
@ -537,7 +537,7 @@ class Document(BaseDocument):
|
|||
self.db_update()
|
||||
|
||||
self.update_children()
|
||||
self.reset_virtual_child_tables()
|
||||
self.reset_computed_child_tables()
|
||||
self.run_post_save_methods()
|
||||
|
||||
# clear unsaved flag
|
||||
|
|
@ -616,9 +616,9 @@ class Document(BaseDocument):
|
|||
d: Document
|
||||
d.db_update()
|
||||
|
||||
def reset_virtual_child_tables(self):
|
||||
"""Reset virtual child tables so that they are reloaded next time"""
|
||||
for df in self.meta.get_table_fields(ignore_virtual=False):
|
||||
def reset_computed_child_tables(self):
|
||||
"""Reset computed child tables so that they are reloaded next time"""
|
||||
for df in self.meta.get_table_fields(include_computed=True):
|
||||
if df.is_virtual:
|
||||
self.__dict__.pop(df.fieldname, None)
|
||||
|
||||
|
|
@ -886,7 +886,7 @@ class Document(BaseDocument):
|
|||
return
|
||||
|
||||
all_fields = self.meta.fields.copy()
|
||||
for table_field in self.meta.get_table_fields(ignore_virtual=False):
|
||||
for table_field in self.meta.get_table_fields(include_computed=True):
|
||||
all_fields += frappe.get_meta(table_field.options).fields or []
|
||||
|
||||
if all(df.permlevel == 0 for df in all_fields):
|
||||
|
|
@ -902,7 +902,7 @@ class Document(BaseDocument):
|
|||
# hasattr might return True for class attribute which can't be delattr-ed.
|
||||
continue
|
||||
|
||||
for table_field in self.meta.get_table_fields(ignore_virtual=False):
|
||||
for table_field in self.meta.get_table_fields(include_computed=True):
|
||||
for df in frappe.get_meta(table_field.options).fields or []:
|
||||
if df.permlevel and df.permlevel not in has_access_to:
|
||||
for child in self.get(table_field.fieldname) or []:
|
||||
|
|
@ -1115,11 +1115,11 @@ class Document(BaseDocument):
|
|||
msg = ", ".join(each[2] for each in cancelled_links)
|
||||
frappe.throw(_("Cannot link cancelled document: {0}").format(msg), frappe.CancelledLinkError)
|
||||
|
||||
def get_all_children(self, parenttype=None, *, ignore_virtual=True) -> list["Document"]:
|
||||
def get_all_children(self, parenttype=None, *, include_computed=False) -> list["Document"]:
|
||||
"""Return all children documents from **Table** type fields in a list."""
|
||||
|
||||
children = []
|
||||
table_fieldnames = self._non_virtual_table_fieldnames if ignore_virtual else self._table_fieldnames
|
||||
table_fieldnames = self._table_fieldnames if include_computed else self._non_computed_table_fieldnames
|
||||
|
||||
for fieldname, child_doctype in table_fieldnames.items():
|
||||
if parenttype and child_doctype != parenttype:
|
||||
|
|
@ -1321,7 +1321,7 @@ class Document(BaseDocument):
|
|||
|
||||
return frappe.clear_last_message()
|
||||
|
||||
for fieldname in self._non_virtual_table_fieldnames:
|
||||
for fieldname in self._non_computed_table_fieldnames:
|
||||
for row in self.get(fieldname):
|
||||
row._doc_before_save = next(
|
||||
(d for d in (self._doc_before_save.get(fieldname) or []) if d.name == row.name), None
|
||||
|
|
|
|||
|
|
@ -221,8 +221,8 @@ class Meta(Document):
|
|||
|
||||
return set_only_once_fields
|
||||
|
||||
def get_table_fields(self, ignore_virtual=True):
|
||||
return self._non_virtual_table_fields if ignore_virtual else self._table_fields
|
||||
def get_table_fields(self, include_computed=False):
|
||||
return self._table_fields if include_computed else self._non_computed_table_fields
|
||||
|
||||
def get_global_search_fields(self):
|
||||
"""Return list of fields with `in_global_search` set and `name` if set."""
|
||||
|
|
@ -490,16 +490,14 @@ class Meta(Document):
|
|||
def _table_fields(self):
|
||||
if self.name == "DocType":
|
||||
return DOCTYPE_TABLE_FIELDS
|
||||
|
||||
return self.get("fields", {"fieldtype": ["in", table_fields]})
|
||||
|
||||
@cached_property
|
||||
def _non_virtual_table_fields(self):
|
||||
def _non_computed_table_fields(self):
|
||||
if self.name == "DocType":
|
||||
return self._table_fields
|
||||
|
||||
if self.get("is_virtual"):
|
||||
return []
|
||||
|
||||
return self.get("fields", {"fieldtype": ["in", table_fields], "is_virtual": 0})
|
||||
|
||||
@cached_property
|
||||
|
|
@ -507,15 +505,15 @@ class Meta(Document):
|
|||
return {field.fieldname: field.options for field in self._table_fields}
|
||||
|
||||
@cached_property
|
||||
def _non_virtual_table_doctypes(self):
|
||||
return {field.fieldname: field.options for field in self._non_virtual_table_fields}
|
||||
def _non_computed_table_doctypes(self):
|
||||
return {field.fieldname: field.options for field in self._non_computed_table_fields}
|
||||
|
||||
def init_field_caches(self):
|
||||
self._fields
|
||||
self._table_fields
|
||||
self._non_virtual_table_fields
|
||||
self._non_computed_table_fields
|
||||
self._table_doctypes
|
||||
self._non_virtual_table_doctypes
|
||||
self._non_computed_table_doctypes
|
||||
|
||||
def sort_fields(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ def export_to_files(record_list=None, record_module=None, verbose=0, create_init
|
|||
|
||||
|
||||
def write_document_file(doc, record_module=None, create_init=True, folder_name=None):
|
||||
doc_export = doc.as_dict(no_nulls=True, ignore_virtual_child_tables=True)
|
||||
doc_export = doc.as_dict(no_nulls=True, ignore_computed_child_tables=True)
|
||||
doc.run_method("before_export", doc_export)
|
||||
|
||||
doc_export = strip_default_fields(doc, doc_export)
|
||||
|
|
|
|||
|
|
@ -803,7 +803,7 @@ def has_child_permission(
|
|||
if parent_meta.istable or not (
|
||||
valid_parentfields := [
|
||||
df.fieldname
|
||||
for df in parent_meta.get_table_fields(ignore_virtual=False)
|
||||
for df in parent_meta.get_table_fields(include_computed=True)
|
||||
if df.options == child_doctype
|
||||
]
|
||||
):
|
||||
|
|
|
|||
|
|
@ -1866,7 +1866,7 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
// returns list of children that are selected. returns [parentfield, name] for each
|
||||
var selected = {},
|
||||
me = this;
|
||||
frappe.meta.get_table_fields(this.doctype, false).forEach(function (df) {
|
||||
frappe.meta.get_table_fields(this.doctype, true).forEach(function (df) {
|
||||
// handle TableMultiselect child fields
|
||||
let _selected = [];
|
||||
|
||||
|
|
@ -1887,7 +1887,7 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
if (frappe.meta.docfield_map[this.doctype][fieldname]) {
|
||||
doctype = this.doctype;
|
||||
} else {
|
||||
frappe.meta.get_table_fields(this.doctype, false).every(function (df) {
|
||||
frappe.meta.get_table_fields(this.doctype, true).every(function (df) {
|
||||
if (frappe.meta.docfield_map[df.options][fieldname]) {
|
||||
doctype = df.options;
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -149,13 +149,13 @@ $.extend(frappe.meta, {
|
|||
return docfield_map && docfield_map[fn];
|
||||
},
|
||||
|
||||
get_table_fields: function (dt, ignore_virtual = true) {
|
||||
get_table_fields: function (dt, include_computed = false) {
|
||||
return $.map(frappe.meta.docfield_list[dt], function (d) {
|
||||
if (!frappe.model.table_fields.includes(d.fieldtype)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ignore_virtual && d.is_virtual) {
|
||||
if (!include_computed && d.is_virtual) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ $.extend(frappe.meta, {
|
|||
// found in parent
|
||||
out = doctype;
|
||||
} else {
|
||||
frappe.meta.get_table_fields(doctype).every(function (d) {
|
||||
frappe.meta.get_table_fields(doctype, true).every(function (d) {
|
||||
if (
|
||||
frappe.meta.has_field(d.options, key) ||
|
||||
frappe.model.child_table_field_list.includes(key)
|
||||
|
|
|
|||
|
|
@ -2150,7 +2150,7 @@ def get_filter(doctype: str, filters: FilterSignature, filters_config=None) -> "
|
|||
meta = frappe.get_meta(f.doctype)
|
||||
if not meta.has_field(f.fieldname):
|
||||
# try and match the doctype name from child tables
|
||||
for df in meta.get_table_fields(ignore_virtual=False):
|
||||
for df in meta.get_table_fields(include_computed=True):
|
||||
if frappe.get_meta(df.options).has_field(f.fieldname):
|
||||
f.doctype = df.options
|
||||
break
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ def set_title_values_for_link_and_dynamic_link_fields(
|
|||
|
||||
|
||||
def set_title_values_for_table_and_multiselect_fields(meta: "Meta", doc: "Document") -> None:
|
||||
for field in meta.get_table_fields(ignore_virtual=False):
|
||||
for field in meta.get_table_fields(include_computed=True):
|
||||
if not doc.get(field.fieldname):
|
||||
continue
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue