[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:
committed by
gawa-odoo
parent
b8dca3b174
commit
1cfc896f2c
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user