refactor!: better API contracts for virtual doctype
Current APIs implement class methods as instance method, which is problamamtic while implementing methods. E.g. If load_from_db doesn't like empty docname then all class method will stop working. This change while breaking is essential for usability of virtual doctype.
This commit is contained in:
parent
b8d56eaefb
commit
e8efd64dbc
4 changed files with 67 additions and 22 deletions
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
46
frappe/model/virtual_doctype.py
Normal file
46
frappe/model/virtual_doctype.py
Normal file
|
|
@ -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."""
|
||||
...
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue