Add json_sql helper function
This commit is contained in:
@@ -4,10 +4,11 @@ Changelog
|
|||||||
Here you can see the full list of changes between each SQLAlchemy-Utils release.
|
Here you can see the full list of changes between each SQLAlchemy-Utils release.
|
||||||
|
|
||||||
|
|
||||||
0.29.2 (2015-01-xx)
|
0.29.2 (2015-01-08)
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
- Removed deprecated defer_except (SQLAlchemy's own load_only should be used from now on)
|
- Removed deprecated defer_except (SQLAlchemy's own load_only should be used from now on)
|
||||||
|
- Added json_sql PostgreSQL helper function
|
||||||
|
|
||||||
|
|
||||||
0.29.1 (2015-01-03)
|
0.29.1 (2015-01-03)
|
||||||
|
@@ -41,6 +41,12 @@ has_unique_index
|
|||||||
.. autofunction:: has_unique_index
|
.. autofunction:: has_unique_index
|
||||||
|
|
||||||
|
|
||||||
|
json_sql
|
||||||
|
^^^^^^^^
|
||||||
|
|
||||||
|
.. autofunction:: json_sql
|
||||||
|
|
||||||
|
|
||||||
render_expression
|
render_expression
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@ from .functions import (
|
|||||||
has_unique_index,
|
has_unique_index,
|
||||||
identity,
|
identity,
|
||||||
is_loaded,
|
is_loaded,
|
||||||
|
json_sql,
|
||||||
merge_references,
|
merge_references,
|
||||||
mock_engine,
|
mock_engine,
|
||||||
naturally_equivalent,
|
naturally_equivalent,
|
||||||
@@ -86,7 +87,7 @@ from .types import (
|
|||||||
from .models import Timestamp
|
from .models import Timestamp
|
||||||
|
|
||||||
|
|
||||||
__version__ = '0.29.1'
|
__version__ = '0.29.2'
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
@@ -14,6 +14,7 @@ from .database import (
|
|||||||
has_index,
|
has_index,
|
||||||
has_unique_index,
|
has_unique_index,
|
||||||
is_auto_assigned_date_column,
|
is_auto_assigned_date_column,
|
||||||
|
json_sql
|
||||||
)
|
)
|
||||||
from .foreign_keys import (
|
from .foreign_keys import (
|
||||||
dependent_objects,
|
dependent_objects,
|
||||||
@@ -65,6 +66,7 @@ __all__ = (
|
|||||||
'is_loaded',
|
'is_loaded',
|
||||||
'is_auto_assigned_date_column',
|
'is_auto_assigned_date_column',
|
||||||
'is_indexed_foreign_key',
|
'is_indexed_foreign_key',
|
||||||
|
'json_sql',
|
||||||
'make_order_by_deterministic',
|
'make_order_by_deterministic',
|
||||||
'mock_engine',
|
'mock_engine',
|
||||||
'naturally_equivalent',
|
'naturally_equivalent',
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import collections
|
||||||
|
import itertools
|
||||||
import os
|
import os
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
@@ -105,6 +107,75 @@ def escape_like(string, escape_char='*'):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def json_sql(value, scalars_to_json=True):
|
||||||
|
"""
|
||||||
|
Convert python data structures to PostgreSQL specific SQLAlchemy JSON
|
||||||
|
constructs. This function is extremly useful if you need to build
|
||||||
|
PostgreSQL JSON on python side.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This function needs PostgreSQL >= 9.4
|
||||||
|
|
||||||
|
Scalars are converted to to_json SQLAlchemy function objects
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
json_sql(1) # Equals SQL: to_json(1)
|
||||||
|
|
||||||
|
json_sql('a') # to_json('a')
|
||||||
|
|
||||||
|
|
||||||
|
Mappings are converted to json_build_object constructs
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
json_sql({'a': 'c', '2': 5}) # json_build_object('a', 'c', '2', 5)
|
||||||
|
|
||||||
|
|
||||||
|
Sequences (other than strings) are converted to json_build_array constructs
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
json_sql([1, 2, 3]) # json_build_array(1, 2, 3)
|
||||||
|
|
||||||
|
|
||||||
|
You can also nest these data structures
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
json_sql({'a': [1, 2, 3]})
|
||||||
|
# json_build_object('a', json_build_array[1, 2, 3])
|
||||||
|
|
||||||
|
|
||||||
|
:param value:
|
||||||
|
value to be converted to SQLAlchemy PostgreSQL function constructs
|
||||||
|
"""
|
||||||
|
scalar_convert = sa.text
|
||||||
|
if scalars_to_json:
|
||||||
|
scalar_convert = lambda a: sa.func.to_json(sa.text(a))
|
||||||
|
|
||||||
|
if isinstance(value, collections.Mapping):
|
||||||
|
return sa.func.json_build_object(
|
||||||
|
*(
|
||||||
|
json_sql(v, scalars_to_json=False)
|
||||||
|
for v in itertools.chain(*value.items())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif isinstance(value, str):
|
||||||
|
return scalar_convert("'{0}'".format(value))
|
||||||
|
elif isinstance(value, collections.Sequence):
|
||||||
|
return sa.func.json_build_array(
|
||||||
|
*(
|
||||||
|
json_sql(v, scalars_to_json=False)
|
||||||
|
for v in value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif isinstance(value, (int, float)):
|
||||||
|
return scalar_convert(str(value))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def has_index(column):
|
def has_index(column):
|
||||||
"""
|
"""
|
||||||
Return whether or not given column has an index. A column has an index if
|
Return whether or not given column has an index. A column has an index if
|
||||||
|
@@ -22,4 +22,3 @@ class TestIsLoaded(object):
|
|||||||
def test_unloaded_property(self):
|
def test_unloaded_property(self):
|
||||||
article = self.Article(id=4)
|
article = self.Article(id=4)
|
||||||
assert not is_loaded(article, 'title')
|
assert not is_loaded(article, 'title')
|
||||||
|
|
||||||
|
31
tests/functions/test_json_sql.py
Normal file
31
tests/functions/test_json_sql.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import pytest
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy_utils import json_sql
|
||||||
|
|
||||||
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestJSONSQL(TestCase):
|
||||||
|
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
('value', 'compiled'),
|
||||||
|
(
|
||||||
|
(1, 'to_json(1)'),
|
||||||
|
(14.14, 'to_json(14.14)'),
|
||||||
|
({'a': 2, 'b': 'c'}, "json_build_object('a', 2, 'b', 'c')"),
|
||||||
|
(
|
||||||
|
{'a': {'b': 'c'}},
|
||||||
|
"json_build_object('a', json_build_object('b', 'c'))"
|
||||||
|
),
|
||||||
|
({}, 'json_build_object()'),
|
||||||
|
([1, 2], 'json_build_array(1, 2)'),
|
||||||
|
([], 'json_build_array()'),
|
||||||
|
(
|
||||||
|
[sa.select([sa.text('1')])],
|
||||||
|
'json_build_array((SELECT 1))'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
def test_compiled_scalars(self, value, compiled):
|
||||||
|
assert str(json_sql(value).compile(self.connection)) == compiled
|
Reference in New Issue
Block a user