Merge pull request #7305 from SaiFi0102/DocType-JSON-Field-Order
feat: DocType JSON changes for cleaner Git Diffs
This commit is contained in:
commit
a6ddb424f3
5 changed files with 204 additions and 4 deletions
|
|
@ -1297,7 +1297,7 @@ def get_value(*args, **kwargs):
|
|||
|
||||
def as_json(obj, indent=1):
|
||||
from frappe.utils.response import json_handler
|
||||
return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler)
|
||||
return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': '))
|
||||
|
||||
def are_emails_muted():
|
||||
from frappe.utils import cint
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ from frappe.desk.notifications import delete_notification_count_for
|
|||
from frappe.modules import make_boilerplate, get_doc_path
|
||||
from frappe.model.db_schema import validate_column_name, validate_column_length, type_map
|
||||
from frappe.model.docfield import supports_translation
|
||||
from frappe.modules.import_file import get_file_path
|
||||
from six import iteritems
|
||||
import frappe.website.render
|
||||
import json
|
||||
|
||||
# imports - third-party imports
|
||||
import pymysql
|
||||
|
|
@ -240,7 +243,8 @@ class DocType(Document):
|
|||
self.update_fields_to_fetch()
|
||||
|
||||
from frappe import conf
|
||||
if not self.custom and not (frappe.flags.in_import or frappe.flags.in_test) and conf.get('developer_mode'):
|
||||
allow_doctype_export = frappe.flags.allow_doctype_export or (not frappe.flags.in_test and conf.get('developer_mode'))
|
||||
if not self.custom and not frappe.flags.in_import and allow_doctype_export:
|
||||
self.export_doc()
|
||||
self.make_controller_template()
|
||||
|
||||
|
|
@ -390,6 +394,72 @@ class DocType(Document):
|
|||
if naming_series[0].default:
|
||||
make_property_setter(self.name, "naming_series", "default", naming_series[0].default, "Text", validate_fields_for_doctype=False)
|
||||
|
||||
def before_export(self, docdict):
|
||||
# remove null and empty fields
|
||||
def remove_null_fields(o):
|
||||
to_remove = []
|
||||
for attr, value in iteritems(o):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
remove_null_fields(v)
|
||||
elif not value:
|
||||
to_remove.append(attr)
|
||||
|
||||
for attr in to_remove:
|
||||
del o[attr]
|
||||
|
||||
remove_null_fields(docdict)
|
||||
|
||||
# retain order of 'fields' table and change order in 'field_order'
|
||||
docdict["field_order"] = [f.fieldname for f in self.fields]
|
||||
|
||||
path = get_file_path(self.module, "DocType", self.name)
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
with open(path, 'r') as txtfile:
|
||||
olddoc = json.loads(txtfile.read())
|
||||
|
||||
old_field_names = [f['fieldname'] for f in olddoc.get("fields", [])]
|
||||
if old_field_names:
|
||||
new_field_dicts = []
|
||||
remaining_field_names = [f.fieldname for f in self.fields]
|
||||
|
||||
for fieldname in old_field_names:
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict['fields']))
|
||||
if field_dict:
|
||||
new_field_dicts.append(field_dict[0])
|
||||
remaining_field_names.remove(fieldname)
|
||||
|
||||
for fieldname in remaining_field_names:
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict['fields']))
|
||||
new_field_dicts.append(field_dict[0])
|
||||
|
||||
docdict['fields'] = new_field_dicts
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def prepare_for_import(docdict):
|
||||
# set order of fields from field_order
|
||||
if docdict.get("field_order"):
|
||||
new_field_dicts = []
|
||||
remaining_field_names = [f['fieldname'] for f in docdict.get('fields', [])]
|
||||
|
||||
for fieldname in docdict.get('field_order'):
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict.get('fields', [])))
|
||||
if field_dict:
|
||||
new_field_dicts.append(field_dict[0])
|
||||
remaining_field_names.remove(fieldname)
|
||||
|
||||
for fieldname in remaining_field_names:
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict.get('fields', [])))
|
||||
new_field_dicts.append(field_dict[0])
|
||||
|
||||
docdict['fields'] = new_field_dicts
|
||||
|
||||
if "field_order" in docdict:
|
||||
del docdict["field_order"]
|
||||
|
||||
def export_doc(self):
|
||||
"""Export to standard folder `[module]/doctype/[name]/[name].json`."""
|
||||
from frappe.modules.export_file import export_to_files
|
||||
|
|
|
|||
|
|
@ -104,4 +104,125 @@ class TestDocType(unittest.TestCase):
|
|||
for depends_on in ["depends_on", "collapsible_depends_on"]:
|
||||
condition = field.get(depends_on)
|
||||
if condition:
|
||||
self.assertFalse(re.match(pattern, condition))
|
||||
self.assertFalse(re.match(pattern, condition))
|
||||
|
||||
def test_sync_field_order(self):
|
||||
from frappe.modules.import_file import get_file_path
|
||||
import os
|
||||
|
||||
# create test doctype
|
||||
test_doctype = frappe.get_doc({
|
||||
"doctype": "DocType",
|
||||
"module": "Core",
|
||||
"fields": [
|
||||
{
|
||||
"label": "Field 1",
|
||||
"fieldname": "field_1",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"label": "Field 2",
|
||||
"fieldname": "field_2",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"label": "Field 3",
|
||||
"fieldname": "field_3",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"label": "Field 4",
|
||||
"fieldname": "field_4",
|
||||
"fieldtype": "Data"
|
||||
}
|
||||
],
|
||||
"permissions": [{
|
||||
"role": "System Manager",
|
||||
"read": 1
|
||||
}],
|
||||
"name": "Test Field Order DocType",
|
||||
"__islocal": 1
|
||||
})
|
||||
|
||||
path = get_file_path(test_doctype.module, test_doctype.doctype, test_doctype.name)
|
||||
initial_fields_order = ['field_1', 'field_2', 'field_3', 'field_4']
|
||||
|
||||
frappe.delete_doc_if_exists("DocType", "Test Field Order DocType")
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
|
||||
try:
|
||||
frappe.flags.allow_doctype_export = 1
|
||||
test_doctype.save()
|
||||
|
||||
# assert that field_order list is being created with the default order
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertTrue(test_doctype_json.get("field_order"))
|
||||
self.assertEqual(len(test_doctype_json['fields']), len(test_doctype_json['field_order']))
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], test_doctype_json['field_order'])
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order)
|
||||
self.assertListEqual(test_doctype_json['field_order'], initial_fields_order)
|
||||
|
||||
# remove field_order to test reload_doc/sync/migrate is backwards compatible without field_order
|
||||
del test_doctype_json['field_order']
|
||||
with open(path, 'w+') as txtfile:
|
||||
txtfile.write(frappe.as_json(test_doctype_json))
|
||||
|
||||
# assert that field_order is actually removed from the json file
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertFalse(test_doctype_json.get("field_order"))
|
||||
|
||||
# make sure that migrate/sync is backwards compatible without field_order
|
||||
frappe.reload_doctype(test_doctype.name, force=True)
|
||||
test_doctype.reload()
|
||||
|
||||
# assert that field_order list is being created with the default order again
|
||||
test_doctype.save()
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertTrue(test_doctype_json.get("field_order"))
|
||||
self.assertEqual(len(test_doctype_json['fields']), len(test_doctype_json['field_order']))
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], test_doctype_json['field_order'])
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order)
|
||||
self.assertListEqual(test_doctype_json['field_order'], initial_fields_order)
|
||||
|
||||
# reorder fields: swap row 1 and 3
|
||||
test_doctype.fields[0], test_doctype.fields[2] = test_doctype.fields[2], test_doctype.fields[0]
|
||||
for i, f in enumerate(test_doctype.fields):
|
||||
f.idx = i + 1
|
||||
|
||||
# assert that reordering fields only affects `field_order` rather than `fields` attr
|
||||
test_doctype.save()
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order)
|
||||
self.assertListEqual(test_doctype_json['field_order'], ['field_3', 'field_2', 'field_1', 'field_4'])
|
||||
|
||||
# reorder `field_order` in the json file: swap row 2 and 4
|
||||
test_doctype_json['field_order'][1], test_doctype_json['field_order'][3] = test_doctype_json['field_order'][3], test_doctype_json['field_order'][1]
|
||||
with open(path, 'w+') as txtfile:
|
||||
txtfile.write(frappe.as_json(test_doctype_json))
|
||||
|
||||
# assert that reordering `field_order` from json file is reflected in DocType upon migrate/sync
|
||||
frappe.reload_doctype(test_doctype.name, force=True)
|
||||
test_doctype.reload()
|
||||
self.assertListEqual([f.fieldname for f in test_doctype.fields], ['field_3', 'field_4', 'field_1', 'field_2'])
|
||||
|
||||
# insert row in the middle and remove first row (field 3)
|
||||
test_doctype.append("fields", {
|
||||
"label": "Field 5",
|
||||
"fieldname": "field_5",
|
||||
"fieldtype": "Data"
|
||||
})
|
||||
test_doctype.fields[4], test_doctype.fields[3] = test_doctype.fields[3], test_doctype.fields[4]
|
||||
test_doctype.fields[3], test_doctype.fields[2] = test_doctype.fields[2], test_doctype.fields[3]
|
||||
test_doctype.remove(test_doctype.fields[0])
|
||||
for i, f in enumerate(test_doctype.fields):
|
||||
f.idx = i + 1
|
||||
|
||||
test_doctype.save()
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], ['field_1', 'field_2', 'field_4', 'field_5'])
|
||||
self.assertListEqual(test_doctype_json['field_order'], ['field_4', 'field_5', 'field_1', 'field_2'])
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
frappe.flags.allow_doctype_export = 0
|
||||
|
|
|
|||
|
|
@ -23,6 +23,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):
|
||||
newdoc = doc.as_dict(no_nulls=True)
|
||||
doc.run_method("before_export", newdoc)
|
||||
|
||||
# strip out default fields from children
|
||||
for df in doc.meta.get_table_fields():
|
||||
|
|
@ -38,7 +39,7 @@ def write_document_file(doc, record_module=None, create_init=True):
|
|||
|
||||
# write the data file
|
||||
fname = scrub(doc.name)
|
||||
with open(os.path.join(folder, fname +".json"),'w+') as txtfile:
|
||||
with open(os.path.join(folder, fname + ".json"), 'w+') as txtfile:
|
||||
txtfile.write(frappe.as_json(newdoc))
|
||||
|
||||
def get_module_name(doc):
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from __future__ import unicode_literals, print_function
|
|||
import frappe, os, json
|
||||
from frappe.modules import get_module_path, scrub_dt_dn
|
||||
from frappe.utils import get_datetime_str
|
||||
from frappe.model.base_document import get_controller
|
||||
|
||||
ignore_values = {
|
||||
"Report": ["disabled", "prepared_report"],
|
||||
|
|
@ -97,8 +98,15 @@ def import_doc(docdict, force=False, data_import=False, pre_process=None,
|
|||
ignore_version=None, reset_permissions=False):
|
||||
frappe.flags.in_import = True
|
||||
docdict["__islocal"] = 1
|
||||
|
||||
controller = get_controller(docdict['doctype'])
|
||||
if controller and hasattr(controller, 'prepare_for_import') and callable(getattr(controller, 'prepare_for_import')):
|
||||
controller.prepare_for_import(docdict)
|
||||
|
||||
doc = frappe.get_doc(docdict)
|
||||
|
||||
doc.run_method("before_import")
|
||||
|
||||
doc.flags.ignore_version = ignore_version
|
||||
if pre_process:
|
||||
pre_process(doc)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue