[IMP] core: add Json field

To be able to create easily a jsonb column in database,
we create a Json type Field. Currently, it is quite limited
field:

- We cannot modified the value in-place, we need to always set
the entire jsonify value.
- No domain operator is done to work with jsonb. Now, it works as a
text field.

closes odoo/odoo#103097

X-original-commit: 7eeba9d205d2dace571b5d0895ddba6290a512db
Related: odoo/enterprise#32729
Signed-off-by: Quentin De Paoli <qdp@odoo.com>
This commit is contained in:
Rémy Voet (ryv)
2022-10-17 10:11:09 +02:00
committed by gawa-odoo
parent b8dca3b174
commit 1cfc896f2c
6 changed files with 69 additions and 4 deletions
+1 -1
View File
@@ -2345,7 +2345,7 @@ class AccountMoveLine(models.Model):
continue
distribution_by_root_plan = {}
for analytic_account_id, percentage in (line.analytic_distribution or {}).items():
root_plan = self.env['account.analytic.account'].browse(analytic_account_id).root_plan_id
root_plan = self.env['account.analytic.account'].browse(int(analytic_account_id)).root_plan_id
distribution_by_root_plan[root_plan.id] = distribution_by_root_plan.get(root_plan.id, 0) + percentage
for plan_id in mandatory_plans_ids:
@@ -97,6 +97,7 @@ class Discussion(models.Model):
important_emails = fields.One2many('test_new_api.emailmessage', 'discussion',
domain=[('important', '=', True)])
history = fields.Json('History', default={'delete_messages': []})
attributes_definition = fields.PropertiesDefinition('Message Properties') # see message@attributes
def _domain_very_important(self):
@@ -6,6 +6,7 @@ from . import test_attributes
from . import test_one2many
from . import test_many2many
from . import test_properties
from . import test_json_field
from . import test_qweb_float
from . import test_indexed_translation
from . import test_related_translation
@@ -0,0 +1,36 @@
from odoo.tests.common import TransactionCase
class JsonFieldTest(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.discussion_1 = cls.env['test_new_api.discussion'].create({
'name': 'Test Discussion JSON field'
})
def test_json_field_read_write(self):
random_str = "tVpajMuSvaR94DwSRVtRSLGNkKViNbWHt2hq13"
random_str_2 = "Crypto-money base"
self.assertEqual(self.discussion_1.history, {'delete_messages': []})
# Check that it is not the value of the cache return by convert_to_record
self.assertIsNot(self.discussion_1.history, self.env.cache.get(self.discussion_1, type(self.discussion_1).history))
self.assertEqual(self.discussion_1.history, {'delete_messages': []})
self.discussion_1.history = {'delete_messages': [random_str]}
self.discussion_1.flush_recordset()
self.assertEqual(self.discussion_1.history, {'delete_messages': [random_str,]})
self.discussion_1.history = {'delete_messages': [random_str, random_str_2]}
self.discussion_1.flush_recordset()
self.assertEqual(self.discussion_1.history, {'delete_messages': [random_str, random_str_2]})
self.discussion_1.history = (random_str, random_str_2)
self.discussion_1.flush_recordset()
self.assertEqual(self.discussion_1.history, [random_str, random_str_2])
@@ -177,7 +177,7 @@ class PropertiesCase(TransactionCase):
'SELECT "test_new_api_message"."id" AS "id", "test_new_api_message"."attributes" AS "attributes" FROM "test_new_api_message" WHERE "test_new_api_message".id IN %s',
'SELECT "test_new_api_message"."id" AS "id", "test_new_api_message"."discussion" AS "discussion", "test_new_api_message"."body" AS "body", "test_new_api_message"."author" AS "author", "test_new_api_message"."name" AS "name", "test_new_api_message"."important" AS "important", "test_new_api_message"."label"->>\'en_US\' AS "label", "test_new_api_message"."priority" AS "priority", "test_new_api_message"."create_uid" AS "create_uid", "test_new_api_message"."create_date" AS "create_date", "test_new_api_message"."write_uid" AS "write_uid", "test_new_api_message"."write_date" AS "write_date" FROM "test_new_api_message" WHERE "test_new_api_message".id IN %s',
# read the definition on the definition record
'SELECT "test_new_api_discussion"."id" AS "id", "test_new_api_discussion"."name" AS "name", "test_new_api_discussion"."moderator" AS "moderator", "test_new_api_discussion"."message_concat" AS "message_concat", "test_new_api_discussion"."attributes_definition" AS "attributes_definition", "test_new_api_discussion"."create_uid" AS "create_uid", "test_new_api_discussion"."create_date" AS "create_date", "test_new_api_discussion"."write_uid" AS "write_uid", "test_new_api_discussion"."write_date" AS "write_date" FROM "test_new_api_discussion" WHERE "test_new_api_discussion".id IN %s',
'SELECT "test_new_api_discussion"."id" AS "id", "test_new_api_discussion"."name" AS "name", "test_new_api_discussion"."moderator" AS "moderator", "test_new_api_discussion"."message_concat" AS "message_concat", "test_new_api_discussion"."history" AS "history", "test_new_api_discussion"."attributes_definition" AS "attributes_definition", "test_new_api_discussion"."create_uid" AS "create_uid", "test_new_api_discussion"."create_date" AS "create_date", "test_new_api_discussion"."write_uid" AS "write_uid", "test_new_api_discussion"."write_date" AS "write_date" FROM "test_new_api_discussion" WHERE "test_new_api_discussion".id IN %s',
# check the many2one existence
'SELECT "test_new_api_partner".id FROM "test_new_api_partner" WHERE "test_new_api_partner".id IN %s',
'SELECT "test_new_api_partner"."id" AS "id", "test_new_api_partner"."name" AS "name", "test_new_api_partner"."create_uid" AS "create_uid", "test_new_api_partner"."create_date" AS "create_date", "test_new_api_partner"."write_uid" AS "write_uid", "test_new_api_partner"."write_date" AS "write_date" FROM "test_new_api_partner" WHERE "test_new_api_partner".id IN %s',
+29 -2
View File
@@ -21,7 +21,7 @@ import warnings
from markupsafe import Markup
import psycopg2
from psycopg2.extras import Json
from psycopg2.extras import Json as PsycopgJson
import pytz
from difflib import get_close_matches
from hashlib import sha256
@@ -1655,7 +1655,7 @@ class _String(Field):
""" Convert from cache_raw value to column value """
if value is None:
return None
return Json(value) if self.translate else value
return PsycopgJson(value) if self.translate else value
def convert_to_cache(self, value, record, validate=True):
if value is None or value is False:
@@ -3143,6 +3143,33 @@ class Many2oneReference(Integer):
return model_ids
class Json(Field):
""" JSON Field that contain unstructured information in jsonb PostgreSQL column.
This field is still in beta
Some features have not been implemented and won't be implemented in stable versions, including:
* searching
* indexing
* mutating the values.
"""
type = 'json'
column_type = ('jsonb', 'jsonb')
def convert_to_record(self, value, record):
""" Return a copy of the value """
return False if value is None else copy.deepcopy(value)
def convert_to_cache(self, value, record, validate=True):
if not value:
return None
return json.loads(json.dumps(value))
def convert_to_column(self, value, record, values=None, validate=True):
if not value:
return None
return PsycopgJson(value)
class Properties(Field):
""" Field that contains a list of properties (aka "sub-field") based on
a definition defined on a container. Properties are pseudo-fields, acting