Added JSONType
This commit is contained in:
3
setup.py
3
setup.py
@@ -35,6 +35,7 @@ extras_require = {
|
|||||||
'flexmock>=0.9.7',
|
'flexmock>=0.9.7',
|
||||||
'psycopg2>=2.4.6',
|
'psycopg2>=2.4.6',
|
||||||
],
|
],
|
||||||
|
'anyjson': ['anyjson>=0.3.3'],
|
||||||
'babel': ['Babel>=1.3'],
|
'babel': ['Babel>=1.3'],
|
||||||
'arrow': ['arrow>=0.3.4'],
|
'arrow': ['arrow>=0.3.4'],
|
||||||
'phone': [
|
'phone': [
|
||||||
@@ -57,7 +58,7 @@ for name, requirements in extras_require.items():
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='SQLAlchemy-Utils',
|
name='SQLAlchemy-Utils',
|
||||||
version='0.19.0',
|
version='0.20.0',
|
||||||
url='https://github.com/kvesteri/sqlalchemy-utils',
|
url='https://github.com/kvesteri/sqlalchemy-utils',
|
||||||
license='BSD',
|
license='BSD',
|
||||||
author='Konsta Vesterinen, Ryan Leckey, Janne Vanhala, Vesa Uimonen',
|
author='Konsta Vesterinen, Ryan Leckey, Janne Vanhala, Vesa Uimonen',
|
||||||
|
51
sqlalchemy_utils/types/json.py
Normal file
51
sqlalchemy_utils/types/json.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
json = None
|
||||||
|
try:
|
||||||
|
import anyjson as json
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
import six
|
||||||
|
from sqlalchemy.dialects.postgresql.base import ischema_names
|
||||||
|
from ..exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
|
|
||||||
|
class PostgresJSONType(sa.types.UserDefinedType):
|
||||||
|
"""
|
||||||
|
Text search vector type for postgresql.
|
||||||
|
"""
|
||||||
|
def get_col_spec(self):
|
||||||
|
return 'json'
|
||||||
|
|
||||||
|
|
||||||
|
ischema_names['json'] = PostgresJSONType
|
||||||
|
|
||||||
|
|
||||||
|
class JSONType(sa.types.TypeDecorator):
|
||||||
|
"Represents an immutable structure as a json-encoded string."
|
||||||
|
|
||||||
|
impl = sa.UnicodeText
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if json is None:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
'JSONType needs anyjson package installed.'
|
||||||
|
)
|
||||||
|
|
||||||
|
def load_dialect_impl(self, dialect):
|
||||||
|
if dialect.name == 'postgresql':
|
||||||
|
# Use the native JSON type.
|
||||||
|
return dialect.type_descriptor(PostgresJSONType())
|
||||||
|
else:
|
||||||
|
return dialect.type_descriptor(self.impl)
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
if value is not None:
|
||||||
|
value = six.text_type(json.dumps(value))
|
||||||
|
return value
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
if value is not None:
|
||||||
|
value = json.loads(value)
|
||||||
|
return value
|
38
tests/types/test_json.py
Normal file
38
tests/types/test_json.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from pytest import mark
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy_utils.types import json
|
||||||
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
@mark.skipif('json.json is None')
|
||||||
|
class TestJSONType(TestCase):
|
||||||
|
def create_models(self):
|
||||||
|
class Document(self.Base):
|
||||||
|
__tablename__ = 'document'
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True)
|
||||||
|
json = sa.Column(json.JSONType)
|
||||||
|
|
||||||
|
self.Document = Document
|
||||||
|
|
||||||
|
def test_parameter_processing(self):
|
||||||
|
document = self.Document(
|
||||||
|
json={'something': 12}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.session.add(document)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
document = self.session.query(self.Document).first()
|
||||||
|
assert document.json == {'something': 12}
|
||||||
|
|
||||||
|
def test_non_ascii_chars(self):
|
||||||
|
document = self.Document(
|
||||||
|
json={'something': u'äääööö'}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.session.add(document)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
document = self.session.query(self.Document).first()
|
||||||
|
assert document.json == {'something': u'äääööö'}
|
Reference in New Issue
Block a user