Only convert dict fields when they are strings

issue6382
review28101002
This commit is contained in:
Sergi Almacellas Abellana committed 2017-06-09 13:22:42 +02:00
1 parent c24bcbdab1
commit b637c503e7
8 files changed
+57 -6

No files matched your search

+2
View File
@@ -1,3 +1,5 @@
* Allow to store Dict as JSON on the database
Version 4.4.0 - 2017-05-01
* Bug fixes (see mercurial logs for details)
* Sanitize path in file_open against suffix (CVE-2017-0360)
+4
View File
@@ -846,6 +846,10 @@ Dict
A dictionary field with predefined keys.
.. note::
It is possible to store the dict as JSON in the database if the backend
supports by manually altering the column type to JSON on the database.
:class:`Dict` has one extra required argument:
.. attribute:: Dict.schema_model
+2 -2
View File
@@ -33,9 +33,9 @@ if minor_version % 2:
name, name, version)
if platform.python_implementation() == 'PyPy':
pg_require = ['psycopg2cffi >= 2.5']
pg_require = ['psycopg2cffi >= 2.5.4']
else:
pg_require = ['psycopg2 >= 2.5']
pg_require = ['psycopg2 >= 2.5.4']
setup(name=name,
version=version,
+9
View File
@@ -5,6 +5,7 @@ import logging
import re
import os
import urllib
import json
from decimal import Decimal
from threading import RLock
@@ -26,11 +27,13 @@ except ImportError:
PYDATE, PYDATETIME, PYTIME, PYINTERVAL = None, None, None, None
from psycopg2 import IntegrityError as DatabaseIntegrityError
from psycopg2 import OperationalError as DatabaseOperationalError
from psycopg2.extras import register_default_json, register_default_jsonb
from sql import Flavor
from trytond.backend.database import DatabaseInterface, SQLType
from trytond.config import config, parse_uri
from trytond.protocols.jsonrpc import JSONDecoder
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
@@ -340,3 +343,9 @@ if PYINTERVAL:
register_type(PYINTERVAL)
register_adapter(float, lambda value: AsIs(repr(value)))
register_adapter(Decimal, lambda value: AsIs(str(value)))
def convert_json(value):
return json.loads(value, object_hook=JSONDecoder())
register_default_json(loads=convert_json)
register_default_jsonb(loads=convert_json)
+6 -3
View File
@@ -25,9 +25,12 @@ class Dict(Field):
def get(self, ids, model, name, values=None):
dicts = dict((id, None) for id in ids)
for value in values or []:
if value[name]:
dicts[value['id']] = json.loads(value[name],
object_hook=JSONDecoder())
data = value[name]
if data:
# If stored as JSON conversion is done on backend
if isinstance(data, basestring):
data = json.loads(data, object_hook=JSONDecoder())
dicts[value['id']] = data
return dicts
def sql_format(self, value):
+1
View File
@@ -108,6 +108,7 @@ def register():
Dict,
DictDefault,
DictRequired,
DictJSONB,
Binary,
BinaryDefault,
BinaryRequired,
+7 -1
View File
@@ -39,7 +39,7 @@ __all__ = [
'Many2ManyFilterDomainRelation',
'Reference', 'ReferenceTarget', 'ReferenceRequired',
'Selection', 'SelectionRequired',
'DictSchema', 'Dict', 'DictDefault', 'DictRequired',
'DictSchema', 'Dict', 'DictDefault', 'DictRequired', 'DictJSONB',
'Binary', 'BinaryDefault', 'BinaryRequired', 'BinaryFileStorage',
'Many2OneDomainValidation', 'Many2OneTarget', 'Many2OneOrderBy',
'Many2OneSearch', 'Many2OneTree', 'Many2OneMPTT', 'Many2OneNoForeignKey',
@@ -782,6 +782,12 @@ class DictRequired(ModelSQL):
dico = fields.Dict(None, 'Test Dict', required=True)
class DictJSONB(ModelSQL):
'Dict JSONB'
__name__ = 'test.dict_jsonb'
dico = fields.Dict('test.dict.schema', 'Test Dict')
class Binary(ModelSQL):
'Binary'
__name__ = 'test.binary'
+26
View File
@@ -21,6 +21,7 @@ from trytond.exceptions import UserError
from trytond.model import fields
from trytond.pool import Pool
from trytond.config import config
from trytond import backend
class FieldsTestCase(unittest.TestCase):
@@ -3124,6 +3125,31 @@ class FieldsTestCase(unittest.TestCase):
self.assertRaises(UserError, DictRequired.create,
[{'dico': {}}])
@with_transaction()
@unittest.skipIf(
backend.name() != 'postgresql', 'jsonb only suported by postgresql')
def test_dict_jsonb(self):
'Test Dict stored as jsonb'
pool = Pool()
Dict = pool.get('test.dict_jsonb')
connection = Transaction().connection
cursor = connection.cursor()
Database = backend.get('Database')
if Database().get_version(connection) < (9, 2):
return
cursor.execute('ALTER TABLE "%s" '
'ALTER COLUMN dico TYPE json USING dico::json' % Dict._table)
dict1, = Dict.create([{
'dico': {'a': 1, 'b': 2},
}])
self.assert_(dict1.dico == {'a': 1, 'b': 2})
Dict.write([dict1], {'dico': {'z': 26}})
self.assert_(dict1.dico == {'z': 26})
@with_transaction()
def test_binary(self):
'Test Binary'