Skip to content

Instantly share code, notes, and snippets.

@MaxMorais
Last active March 27, 2024 18:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MaxMorais/9ae255a7df742cff4d3adb58a0daea70 to your computer and use it in GitHub Desktop.
Save MaxMorais/9ae255a7df742cff4d3adb58a0daea70 to your computer and use it in GitHub Desktop.
import frappe
from frappe.model.document import Document
from frappe.utils.nestedset import NestedSet
from six import string_types
from frappe.utils import getdate, nowdate
status_map = {}
class SugaredDocument(Document):
def __init__(self, *args, **kwargs):
super(SugaredDocument, self).__init__(*args, **kwargs)
for link in self.meta.get_link_fields():
super(SugaredDocument, self).__setattr__(
link.fieldname,
LinkDescriptor(link.options, link.fieldname, self.get(link.fieldname))
)
for link in self.meta.get_dynamic_link_fields():
super(SugaredDocument, self).__setattr__(
link.fieldname,
DynamicLinkDescriptor(link.options, link.fieldname, self.__dict__.get(link.fieldname))
)
for vlink, dt in getattr(self, 'virtual_links', {}).items():
super(SugaredDocument, self).__setattr__(
vlink,
DynamicLinkDescriptor(dt, vlink, self.__dict__.get(vlink))
)
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get('amended_from'):
self.status = 'Draft'
return
if self.doctype in status_map:
_status = self.status
if status and update:
self.db_set("status", status)
sl = status_map[self.doctype][:]
sl.reverse()
s = []
for s in sl:
if not s[1]:
self.status = s[0]
break
elif s[1].startswith("eval:"):
if frappe.safe_eval(s[1][5:], None, { "self": self.as_dict(), "getdate": getdate,
"nowdate": nowdate, "get_value": frappe.db.get_value , 'len': len}):
self.status = s[0]
break
elif getattr(self, s[1])():
self.status = s[0]
break
if self.status != _status and len(s) > 2 and s[2]:
self.add_comment("Label", _(self.status))
if update:
self.db_set('status', self.status, update_modified = update_modified)
def __getattribute__(self, name):
value = object.__getattribute__(self, name)
if hasattr(value, '__get__') and not callable(value):
value = value.__get__(self, self.__class__)
return value
def __setattribute__(self, name, value):
try:
obj = object.__getattribute__(self, name)
except AttributeError:
pass
else:
if hasattr(obj, '__set__'):
return obj.__set__(self, value)
return object.__setattr__(self, name, value)
__setattr__ = __setattribute__
def get(self, key=None, filters=None, limit=None, default=None):
ret = super(SugaredDocument, self).get(key, filters, limit, default)
if isinstance(ret, (LinkDescriptor, DynamicLinkDescriptor)):
return ret.value
return ret
def set(self, key, value, as_value=False):
if isinstance(value, list) and not as_value:
self.__dict__[key] = []
self.extend(key, value)
else:
obj = super(SugaredDocument, self).get(key)
if isinstance(obj, (LinkDescriptor, DynamicLinkDescriptor)):
setattr(self, key, value)
else:
self.__dict__[key] = value
def __eq__(self, other):
if isinstance(other, string_types) and not self.is_new():
return self.name == other
elif isinstance(other, Document) and not other.is_new() and not self.is_new():
return self.doctype == other.doctype and self.name == other.name
return False
def __ne__(self, other):
return not self.__eq__(other)
def __str__(self):
return self.name
def __unicode__(self):
return self.name
def truth(self):
return bool(
self.doctype and self.name \
and frappe.db.exists(self.doctype, self.name))
class SugaredNestedSet(NestedSet, SugaredDocument):
pass
class LinkDescriptor(object):
def __init__(self, doctype, fieldname, value=None):
self.doctype = doctype
self.fieldname = fieldname
self.value = value
self.__doc = None
def __get__(self, instance, owner):
if self.value is None:
return None
elif self.__doc is None:
self.__doc = frappe.get_doc(self.doctype, self.value)
return self.__doc
def __set__(self, instance, value):
self.value = value
if self.__doc:
self.__doc = None
class DynamicLinkDescriptor(object):
def __init__(self, reference, fieldname, value=None):
self.reference = reference
self.fieldname = fieldname
self.value = value
self.__doc = None
def __get__(self, instance, owner):
dt = instance.get(self.reference)
if not all([self.__doc, dt, self.value]):
return None
elif not self.__doc and all(dt, self.value):
self.__doc = frappe.get_doc(dt, self.value)
return self.__doc
def __set__(self, instance, value):
self.value = value
if self.__doc:
self.__doc = Nonee
from sugared_document import SugaredDocument
# Normal doctypes, childrens and singles
class MyClass(SugaredDocument):
pass
>>> doc = frappe.get_doc('MyClass', 'MyID')
>>> doc.link_field.name
'Something'
>>> doc.link_field.inner_field
'Else'
>>> doc.link_field.creation
datetime(2010, 1, 31, 12, 25, 35, 84526)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment