Only convert dict fields when they are strings
issue6382 review28101002
This commit is contained in:
1 parent
c24bcbdab1
commit
b637c503e7
8 files changed
+57
-6
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
@@ -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):
|
||||
|
||||
@@ -108,6 +108,7 @@ def register():
|
||||
Dict,
|
||||
DictDefault,
|
||||
DictRequired,
|
||||
DictJSONB,
|
||||
Binary,
|
||||
BinaryDefault,
|
||||
BinaryRequired,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in new issue
Block a user