diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 3267429298..9e9aaf489b 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -99,7 +99,7 @@ "label": "Type", "oldfieldname": "fieldtype", "oldfieldtype": "Select", - "options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime", + "options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nJSON\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime", "reqd": 1, "search_index": 1 }, @@ -547,7 +547,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-02-14 11:56:19.812863", + "modified": "2022-03-02 17:07:32.117897", "modified_by": "Administrator", "module": "Core", "name": "DocField", diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py index 031cbb553f..7b4806da59 100644 --- a/frappe/core/doctype/doctype/test_doctype.py +++ b/frappe/core/doctype/doctype/test_doctype.py @@ -538,6 +538,35 @@ class TestDocType(unittest.TestCase): # cleanup dt.delete(ignore_permissions=True) + def test_json_field(self): + """Test json field.""" + import json + + json_doc = new_doctype( + "Test Json Doctype", + fields=[{"label": "json field", "fieldname": "test_json_field", "fieldtype": "JSON"}], + ) + json_doc.insert() + json_doc.save() + doc = frappe.get_doc("DocType", "Test Json Doctype") + for field in doc.fields: + if field.fieldname == "test_json_field": + self.assertEqual(field.fieldtype, "JSON") + break + + doc = frappe.get_doc( + {"doctype": "Test Json Doctype", "test_json_field": json.dumps({"hello": "world"})} + ) + doc.insert() + doc.save() + + test_json = frappe.get_doc("Test Json Doctype", doc.name) + + if isinstance(test_json.test_json_field, str): + test_json.test_json_field = json.loads(test_json.test_json_field) + + self.assertEqual(test_json.test_json_field["hello"], "world") + def new_doctype( name, unique: bool = False, depends_on: str = "", fields: Optional[List[Dict]] = None, **kwargs diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index dcb88d648b..1ae3fd8a61 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -54,6 +54,7 @@ class MariaDBDatabase(Database): "Duration": ("decimal", "21,9"), "Icon": ("varchar", self.VARCHAR_LEN), "Autocomplete": ("varchar", self.VARCHAR_LEN), + "JSON": ("json", ""), } def get_connection(self): diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index c34c20ee7b..228d0f48be 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -66,6 +66,7 @@ class PostgresDatabase(Database): "Duration": ("decimal", "21,9"), "Icon": ("varchar", self.VARCHAR_LEN), "Autocomplete": ("varchar", self.VARCHAR_LEN), + "JSON": ("json", ""), } def get_connection(self): diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 6c9bab2dff..bd607e7119 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -37,6 +37,7 @@ data_fieldtypes = ( "Duration", "Icon", "Autocomplete", + "JSON", ) attachment_fieldtypes = ( diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index d73464cb06..4f2ddd3bb6 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -1,6 +1,7 @@ # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import datetime +import json import frappe from frappe import _ @@ -287,6 +288,9 @@ class BaseDocument(object): elif df.fieldtype == "Int" and not isinstance(d[fieldname], int): d[fieldname] = cint(d[fieldname]) + elif df.fieldtype == "JSON" and isinstance(d[fieldname], dict): + d[fieldname] = json.dumps(d[fieldname], sort_keys=True, indent=4, separators=(",", ": ")) + elif df.fieldtype in ("Currency", "Float", "Percent") and not isinstance(d[fieldname], float): d[fieldname] = flt(d[fieldname]) diff --git a/frappe/public/js/frappe/form/controls/control.js b/frappe/public/js/frappe/form/controls/control.js index 90e8697b1c..8db382dd91 100644 --- a/frappe/public/js/frappe/form/controls/control.js +++ b/frappe/public/js/frappe/form/controls/control.js @@ -39,6 +39,7 @@ import './multiselect_list'; import './rating'; import './duration'; import './icon'; +import './json'; frappe.ui.form.make_control = function (opts) { var control_class_name = "Control" + opts.df.fieldtype.replace(/ /g, ""); diff --git a/frappe/public/js/frappe/form/controls/json.js b/frappe/public/js/frappe/form/controls/json.js new file mode 100644 index 0000000000..ce2e0bd087 --- /dev/null +++ b/frappe/public/js/frappe/form/controls/json.js @@ -0,0 +1,6 @@ +frappe.ui.form.ControlJSON = class ControlCode extends frappe.ui.form.ControlCode { + set_language() { + this.editor.session.setMode('ace/mode/json'); + this.editor.setKeyboardHandler('ace/keyboard/vscode'); + } +};