Pārlūkot izejas kodu

Added Set Defaults, Mandatory Validation and Link Validation in Document

version-14
Anand Doshi pirms 11 gadiem
vecāks
revīzija
01d65fd497
7 mainītis faili ar 217 papildinājumiem un 55 dzēšanām
  1. +4
    -0
      frappe/__init__.py
  2. +1
    -0
      frappe/exceptions.py
  3. +19
    -5
      frappe/model/doctype.py
  4. +154
    -36
      frappe/model/document.py
  5. +2
    -10
      frappe/model/meta.py
  6. +3
    -3
      frappe/model/naming.py
  7. +34
    -1
      frappe/tests/test_document.py

+ 4
- 0
frappe/__init__.py Parādīt failu

@@ -348,6 +348,10 @@ def get_doclist(doctype, name=None):
def get_doctype(doctype, processed=False):
import frappe.model.doctype
return frappe.model.doctype.get(doctype, processed)
def get_meta(doctype, processed=False):
import frappe.model.doctype
return frappe.model.doctype.get_meta(doctype, processed)

def delete_doc(doctype=None, name=None, doclist = None, force=0, ignore_doctypes=None,
for_reload=False, ignore_permissions=False):


+ 1
- 0
frappe/exceptions.py Parādīt failu

@@ -34,3 +34,4 @@ class MandatoryError(ValidationError): pass
class InvalidSignatureError(ValidationError): pass
class RateLimitExceededError(ValidationError): pass
class CannotChangeConstantError(ValidationError): pass
class LinkValidationError(ValidationError): pass

+ 19
- 5
frappe/model/doctype.py Parādīt failu

@@ -27,6 +27,16 @@ docfield_types = frappe.local('doctype_docfield_types')
# doctype_cache = {}
# docfield_types = None

def get_meta(doctype, processed=False, cached=True):
meta = []
for d in get(doctype=doctype, processed=processed, cached=cached):
if d.doctype=="DocType" and d.name==doctype:
meta.append(d)
elif d.parent and d.parent==doctype:
meta.append(d)
return DocTypeDocList(meta)

def get(doctype, processed=False, cached=True):
"""return doclist"""
if cached:
@@ -333,12 +343,12 @@ def get_property(dt, prop, fieldname=None):
return doctypelist[0].fields.get(prop)
def get_link_fields(doctype):
"""get docfields of links and selects with "link:" """
"""get docfields of links and selects with "link:"
for main doctype and child doctypes"""
doctypelist = get(doctype)
return doctypelist.get({"fieldtype":"Link"}).extend(doctypelist.get({"fieldtype":"Select",
return doctypelist.get({"fieldtype":"Link"}).extend(doctypelist.get({"fieldtype":"Select",
"options": "^link:"}))
def add_validators(doctype, doclist):
for validator in frappe.db.sql("""select name from `tabDocType Validator` where
for_doctype=%s""", (doctype,), as_dict=1):
@@ -416,4 +426,8 @@ class DocTypeDocList(frappe.model.doclist.DocList):
def get_permissions(self, user=None):
user_roles = frappe.get_roles(user)
return [p for p in self.get({"doctype": "DocPerm"})
if cint(p.permlevel)==0 and (p.role=="All" or p.role in user_roles)]
if cint(p.permlevel)==0 and (p.role=="All" or p.role in user_roles)]
def get_link_fields(self):
return self.get({"fieldtype":"Link", "parent": self[0].name})\
.extend(self.get({"fieldtype":"Select", "options": "^link:", "parent": self[0].name}))

+ 154
- 36
frappe/model/document.py Parādīt failu

@@ -3,6 +3,7 @@

from __future__ import unicode_literals
import frappe
from frappe import _, msgprint
from frappe.utils import cint, flt
from frappe.model import default_fields
from frappe.model.db_schema import type_map
@@ -22,9 +23,11 @@ class BaseDocument(object):
def __getattr__(self, key):
if self.__dict__.has_key(key):
return self.__dict__[key]
if key != "_table_columns" and key in self.get_table_columns():

if key in self.get_table_columns():
return None
raise AttributeError, key

raise AttributeError(key)

def update(self, d):
if "doctype" in d:
@@ -39,19 +42,16 @@ class BaseDocument(object):
return self.__dict__
def set(self, key, value):
if isinstance(value, list):
if isinstance(value, dict):
# appending
if not self.get(key):
self.__dict__[key] = []
self.get(key).append(self._init_child(value, key))
elif isinstance(value, list):
for v in value:
self.set(key, v)
return
else:
if isinstance(value, dict):
# appending
if not self.get(key):
self.__dict__[key] = []
self.get(key).append(self._init_child(value, key))
return
self.__dict__[key] = value
self.__dict__[key] = value
def _init_child(self, value, key):
if not self.doctype:
@@ -71,11 +71,10 @@ class BaseDocument(object):
return value

@property
def meta(self):
if not self.get("_meta"):
self._meta = frappe.get_doctype(self.doctype)
self._meta = frappe.get_meta(self.doctype)
return self._meta
def get_valid_dict(self):
@@ -92,7 +91,7 @@ class BaseDocument(object):
if not hasattr(self, "_table_columns"):
doctype = self.__dict__.get("doctype")
self._table_columns = default_fields[1:] + \
[df.fieldname for df in frappe.get_doctype(doctype).get_docfields()
[df.fieldname for df in frappe.get_meta(doctype).get_docfields()
if df.fieldtype in type_map]
return self._table_columns
@@ -118,42 +117,103 @@ class BaseDocument(object):
if self.docstatus is not None:
self.docstatus = cint(self.docstatus)
def set_missing_values(self, d):
for key, value in d.iteritems():
if self.get(key) is None:
self.set(key, value)
def get_missing_mandatory_fields(self):
"""Get mandatory fields that do not have any values"""
def get_msg(df):
if df.fieldtype == "Table":
return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label))
elif self.parentfield:
return "{}: {} #{}: {}: {}".format(_("Error"), _("Row"), self.idx,
_("Value missing for"), _(df.label))

else:
return "{}: {}: {}".format(_("Error"), _("Value missing for"), _(df.label))
missing = []
for df in self.meta.get({"doctype": "DocField", "reqd": 1}):
if self.get(df.fieldname) in (None, []):
missing.append((df.fieldname, get_msg(df)))
return missing
def get_invalid_links(self):
def get_msg(df, docname):
if self.parentfield:
return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname)
else:
return "{}: {}".format(_(df.label), docname)
invalid_links = []
for df in self.meta.get_link_fields():
doctype = df.options
if not doctype:
frappe.throw("Options not set for link field: {}".format(df.fieldname))
elif doctype.lower().startswith("link:"):
doctype = doctype[5:]
docname = self.get(df.fieldname)
if docname and not frappe.db.get_value(doctype, docname):
invalid_links.append((df.fieldname, docname, get_msg(df, docname)))
return invalid_links
class Document(BaseDocument):
def __init__(self, arg1, arg2=None):
self.doctype = self.name = None
if isinstance(arg1, basestring) and not arg2:
# single
self.doctype = self.name = arg1
if arg1 and isinstance(arg1, basestring) and arg2:
self.doctype = arg1
if isinstance(arg2, dict):
# filter
self.name = frappe.db.get_value(arg1, arg2, "name")
if self.name is None:
raise frappe.DoesNotExistError
if arg1 and isinstance(arg1, basestring):
if not arg2:
# single
self.doctype = self.name = arg1
else:
self.name = arg2
self.doctype = arg1
if isinstance(arg2, dict):
# filter
self.name = frappe.db.get_value(arg1, arg2, "name")
if self.name is None:
raise frappe.DoesNotExistError
else:
self.name = arg2
self.load_from_db()

elif isinstance(arg1, dict):
super(Document, self).__init__(arg1)
else:
# incorrect arguments. let's not proceed.
raise frappe.DataError("Document({0}, {1})".format(arg1, arg2))

def load_from_db(self):
if self.meta[0].issingle:
self.update(frappe.db.get_singles_dict(self.doctype))
self.fix_numeric_types()
else:
d = frappe.db.get_value(self.doctype, self.name, "*", as_dict=1)
for df in self.meta.get({"doctype":"DocField", "fieldtype":"Table"}):
d[df.fieldname] = frappe.db.get_values(df.options,
{"parent": self.name}, "*", as_dict=True)
{"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname},
"*", as_dict=True)
self.update(d)
def insert(self):
# check links
# check permissions
self.set_defaults()
self._validate()
# run validate, on update etc.
# parent
if self.meta[0].issingle:
self.update_single(self.get_valid_dict())
@@ -161,13 +221,9 @@ class Document(BaseDocument):
self.insert_row()
# children
for df in self.meta.get({"fieldtype":"Table"}):
value = self.get(df.fieldname)
if isinstance(value, list):
for d in value:
d.parent = self.name
print d.__dict__
d.insert_row()
for d in self.get_all_children():
d.parent = self.name
d.insert_row()

def update_single(self, d):
frappe.db.sql("""delete from tabSingles where doctype=%s""", d.get("doctype"))
@@ -176,5 +232,67 @@ class Document(BaseDocument):
frappe.db.sql("""insert into tabSingles(doctype, field, value)
values (%s, %s, %s)""", (d.get("doctype", field, value)))
def set_defaults(self):
if frappe.flags.in_import:
return
new_doc = frappe.new_doc(self.doctype).fields
self.set_missing_values(new_doc)

# children
for df in self.meta.get({"fieldtype":"Table"}):
new_doc = frappe.new_doc(df.options).fields
value = self.get(df.fieldname)
if isinstance(value, list):
for d in value:
d.set_missing_values(new_doc)

def _validate(self):
self.trigger("validate")
self.validate_mandatory()
self.validate_links()
# check restrictions
def validate_mandatory(self):
if self.get("ignore_mandatory"):
return
missing = self.get_missing_mandatory_fields()
for d in self.get_all_children():
missing.extend(d.get_missing_mandatory_fields())
if not missing:
return
for fieldname, msg in missing:
msgprint(msg)
raise frappe.MandatoryError(", ".join((each[0] for each in missing)))
def validate_links(self):
if self.get("ignore_links"):
return
invalid_links = self.get_invalid_links()
for d in self.get_all_children():
invalid_links.extend(d.get_invalid_links())
if not invalid_links:
return
msg = ", ".join((each[2] for each in invalid_links))
frappe.throw("{}: {}".format(_("Could not find the following documents"), msg),
frappe.LinkValidationError)
def get_all_children(self):
ret = []
for df in self.meta.get({"fieldtype": "Table"}):
value = self.get(df.fieldname)
if isinstance(value, list):
ret.extend(value)
return ret
def trigger(self, func, *args, **kwargs):
return

+ 2
- 10
frappe/model/meta.py Parādīt failu

@@ -28,16 +28,8 @@ def get_link_fields(doctype):
"""
import frappe.model.doctype
doclist = frappe.model.doctype.get(doctype)
return [
(d.fields.get('fieldname'), d.fields.get('options'), d.fields.get('label'))
for d in doclist
if d.fields.get('doctype') == 'DocField' and d.fields.get('parent') == doctype
and d.fields.get('fieldname')!='owner'
and (d.fields.get('fieldtype') == 'Link' or
( d.fields.get('fieldtype') == 'Select'
and (d.fields.get('options') or '').startswith('link:'))
)
]
return [(d.fields.get('fieldname'), d.fields.get('options'), d.fields.get('label'))
for d in doclist.get_link_fields() if d.fields.get('fieldname')!='owner']

def get_table_fields(doctype):
child_tables = [[d[0], d[1]] for d in frappe.db.sql("""select options, fieldname


+ 3
- 3
frappe/model/naming.py Parādīt failu

@@ -44,11 +44,11 @@ def set_new_name(doc):
doc.name = make_autoname(autoname, doc.doctype)
# given
elif doc.fields.get('__newname',''):
doc.name = doc.fields['__newname']
elif doc.get('__newname', None):
doc.name = doc.get('__newname')

# default name for table
elif doc.meta.istable:
elif doc.meta[0].istable:
doc.name = make_autoname('#########', doc.doctype)
# unable to determine a name, use global series


+ 34
- 1
frappe/tests/test_document.py Parādīt failu

@@ -32,6 +32,9 @@ class TestDocument(unittest.TestCase):
self.assertTrue(d.name.startswith("EV"))
self.assertEquals(frappe.db.get_value("Event", d.name, "subject"),
"_Test Event 1")
# test if default values are added
self.assertEquals(d.send_reminder, 1)
def test_insert_with_child(self):
d = Document({
@@ -49,6 +52,36 @@ class TestDocument(unittest.TestCase):
self.assertTrue(d.name.startswith("EV"))
self.assertEquals(frappe.db.get_value("Event", d.name, "subject"),
"_Test Event 2")
d1 = Document("Event", d.name)
self.assertTrue(d1.event_individuals[0].person, "Administrator")

def test_mandatory(self):
d = Document({
"doctype": "User",
"email": "test_mandatory@example.com",
})
self.assertRaises(frappe.MandatoryError, d.insert)
d.set("first_name", "Test Mandatory")
d.insert()
self.assertEquals(frappe.db.get_value("User", d.name), d.name)
def test_link_validation(self):
d = Document({
"doctype": "User",
"email": "test_link_validation@example.com",
"first_name": "Link Validation",
"user_roles": [
{
"role": "ABC"
}
]
})
self.assertRaises(frappe.LinkValidationError, d.insert)
d.user_roles = []
d.set("user_roles", {
"role": "System Manager"
})
d.insert()
self.assertEquals(frappe.db.get_value("User", d.name), d.name)

Notiek ielāde…
Atcelt
Saglabāt