diff --git a/frappe/core/doctype/test/test.py b/frappe/core/doctype/test/test.py index c3b8f6accf..e17b3a0a4a 100644 --- a/frappe/core/doctype/test/test.py +++ b/frappe/core/doctype/test/test.py @@ -8,7 +8,7 @@ from frappe.model.document import Document class test(Document): - def db_insert(self): + def db_insert(self, *args, **kwargs): d = self.get_valid_dict(convert_dates_to_str=True) with open("data_file.json", "w+") as read_file: json.dump(d, read_file) @@ -18,26 +18,22 @@ class test(Document): d = json.load(read_file) super(Document, self).__init__(d) - def db_update(self): + def db_update(self, *args, **kwargs): d = self.get_valid_dict(convert_dates_to_str=True) with open("data_file.json", "w+") as read_file: json.dump(d, read_file) - def get_list(self, args): + @staticmethod + def get_list(args): with open("data_file.json") as read_file: return [frappe._dict(json.load(read_file))] - def get_value(self, fields, filters, **kwargs): - # return [] - with open("data_file.json") as read_file: - return [json.load(read_file)] + @staticmethod + def get_count(args): + return 5 - def get_count(self, args): - # return [] - with open("data_file.json") as read_file: - return [json.load(read_file)] - - def get_stats(self, args): + @staticmethod + def get_stats(args): # return [] with open("data_file.json") as read_file: return [json.load(read_file)] diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 9500023ffc..679b052baf 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -24,7 +24,7 @@ def get(): # If virtual doctype get data from controller het_list method if is_virtual_doctype(args.doctype): controller = get_controller(args.doctype) - data = compress(controller(args.doctype).get_list(args)) + data = compress(controller.get_list(args)) else: data = compress(execute(**args), args=args) return data @@ -37,7 +37,7 @@ def get_list(): if is_virtual_doctype(args.doctype): controller = get_controller(args.doctype) - data = controller(args.doctype).get_list(args) + data = controller.get_list(args) else: # uncompressed (refactored from frappe.model.db_query.get_list) data = execute(**args) @@ -52,7 +52,7 @@ def get_count(): if is_virtual_doctype(args.doctype): controller = get_controller(args.doctype) - data = controller(args.doctype).get_count(args) + data = controller.get_count(args) else: distinct = "distinct " if args.distinct == "true" else "" args.fields = [f"count({distinct}`tab{args.doctype}`.name) as total_count"] @@ -528,7 +528,7 @@ def get_sidebar_stats(stats, doctype, filters=None): if is_virtual_doctype(doctype): controller = get_controller(doctype) args = {"stats": stats, "filters": filters} - data = controller(doctype).get_stats(args) + data = controller.get_stats(args) else: data = get_stats(stats, doctype, filters) diff --git a/frappe/model/virtual_doctype.py b/frappe/model/virtual_doctype.py new file mode 100644 index 0000000000..a21d88e605 --- /dev/null +++ b/frappe/model/virtual_doctype.py @@ -0,0 +1,46 @@ +from typing import Protocol + + +class VirtualDoctype(Protocol): + """This class documents requirements that must be met by a doctype controller to function as virtual doctype + + + Additional requirements: + - DocType controller has to inherit from `frappe.model.document.Document` class + + Note: + - "Backend" here means any storage service, it can be a database, flat file or network call to API. + """ + + # ============ class/static methods ============ + + @staticmethod + def get_list(args): + """Similar to reportview.get_list""" + ... + + @staticmethod + def get_count(args) -> int: + """Similar to reportview.get_count, return total count of documents on listview.""" + ... + + @staticmethod + def get_stats(args): + """Similar to reportview.get_stats, return sidebar stats.""" + ... + + # ============ instance methods ============ + + def db_insert(self, *args, **kwargs) -> None: + """Serialize the `Document` object and insert it in backend.""" + ... + + def load_from_db(self) -> None: + """Using self.name initialize current document from backend data. + + This is responsible for updatinng __dict__ of class with all the fields on doctype.""" + ... + + def db_update(self, *args, **kwargs): + """Serialize the `Document` object and update existing document in backend.""" + ... diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index f4a386cfc9..a87ac1b3db 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -296,22 +296,25 @@ def make_boilerplate(template, doc, opts=None): custom_controller = "pass" if doc.get("is_virtual"): custom_controller = """ - def db_insert(self): + def db_insert(self, *args, **kwargs): pass def load_from_db(self): pass - def db_update(self): + def db_update(self, *args, **kwargs): pass - def get_list(self, args): + @staticmethod + def get_list(args): pass - def get_count(self, args): + @staticmethod + def get_count(args): pass - def get_stats(self, args): + @staticmethod + def get_stats(args): pass""" with open(target_file_path, "w") as target: