The dialects that support json are supposed to take arguments
``json_serializer`` and ``json_deserializer`` at the create_engine() level,
however the SQLite dialect calls them ``_json_serilizer`` and
``_json_deserilalizer``. The names have been corrected, the old names are
accepted with a change warning, and these parameters are now documented as
:paramref:`.create_engine.json_serializer` and
:paramref:`.create_engine.json_deserializer`.
Fixes: #4798
Change-Id: I1dbfe439b421fe9bb7ff3594ef455af8156f8851
(cherry picked from commit
104e6907284e602a8485f32fc67fd6af0c00e4d0)
--- /dev/null
+.. change::
+ :tags: bug, sqlite
+ :tickets: 4798
+
+ The dialects that support json are supposed to take arguments
+ ``json_serializer`` and ``json_deserializer`` at the create_engine() level,
+ however the SQLite dialect calls them ``_json_serilizer`` and
+ ``_json_deserilalizer``. The names have been corrected, the old names are
+ accepted with a change warning, and these parameters are now documented as
+ :paramref:`.create_engine.json_serializer` and
+ :paramref:`.create_engine.json_deserializer`.
+
_broken_fk_pragma_quotes = False
_broken_dotted_colnames = False
+ @util.deprecated_params(
+ _json_serializer=(
+ "1.3.7",
+ "The _json_serializer argument to the SQLite dialect has "
+ "been renamed to the correct name of json_serializer. The old "
+ "argument name will be removed in a future release.",
+ ),
+ _json_deserializer=(
+ "1.3.7",
+ "The _json_deserializer argument to the SQLite dialect has "
+ "been renamed to the correct name of json_deserializer. The old "
+ "argument name will be removed in a future release.",
+ ),
+ )
def __init__(
self,
isolation_level=None,
native_datetime=False,
+ json_serializer=None,
+ json_deserializer=None,
_json_serializer=None,
_json_deserializer=None,
**kwargs
):
default.DefaultDialect.__init__(self, **kwargs)
self.isolation_level = isolation_level
- self._json_serializer = _json_serializer
- self._json_deserializer = _json_deserializer
+
+ if _json_serializer:
+ json_serializer = _json_serializer
+ if _json_deserializer:
+ json_deserializer = _json_deserializer
+ self._json_serializer = json_serializer
+ self._json_deserializer = json_deserializer
# this flag used by pysqlite dialect, and perhaps others in the
# future, to indicate the driver is handling date/timestamp
:ref:`session_transaction_isolation` - for the ORM
+ :param json_deserializer: for dialects that support the :class:`.JSON`
+ datatype, this is a Python callable that will convert a JSON string
+ to a Python object. By default, the Python ``json.loads`` function is
+ used.
+
+ .. versionchanged:: 1.3.7 The SQLite dialect renamed this from
+ ``_json_deserializer``.
+
+ :param json_serializer: for dialects that support the :class:`.JSON`
+ datatype, this is a Python callable that will render a given object
+ as JSON. By default, the Python ``json.dumps`` function is used.
+
+ .. versionchanged:: 1.3.7 The SQLite dialect renamed this from
+ ``_json_serializer``.
+
:param label_length=None: optional integer value which limits
the size of dynamically generated column labels to that many
characters. If less than 6, labels are generated as
values, but care must be taken as to the value of the
:paramref:`.JSON.none_as_null` in these cases.
+ The JSON serializer and deserializer used by :class:`.JSON` defaults to
+ Python's ``json.dumps`` and ``json.loads`` functions; in the case of the
+ psycopg2 dialect, psycopg2 may be using its own custom loader function.
+
+ In order to affect the serializer / deserializer, they are currently
+ configurable at the :func:`.create_engine` level via the
+ :paramref:`.create_engine.json_serializer` and
+ :paramref:`.create_engine.json_deserializer` parameters. For example,
+ to turn off ``ensure_ascii``::
+
+ engine = create_engine(
+ "sqlite://",
+ json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False))
+
+ .. versionchanged:: 1.3.7
+
+ SQLite dialect's ``json_serializer`` and ``json_deserializer``
+ parameters renamed from ``_json_serializer`` and
+ ``_json_deserializer``.
+
.. seealso::
:class:`.postgresql.JSON`
import datetime
import decimal
+import json
+
+import mock
from .. import config
+from .. import engines
from .. import fixtures
from ..assertions import eq_
from ..config import requirements
eq_(row, (data_element,))
+ def test_round_trip_custom_json(self):
+ data_table = self.tables.data_table
+ data_element = self.data1
+
+ js = mock.Mock(side_effect=json.dumps)
+ jd = mock.Mock(side_effect=json.loads)
+ engine = engines.testing_engine(
+ options=dict(json_serializer=js, json_deserializer=jd)
+ )
+
+ # support sqlite :memory: database...
+ data_table.create(engine, checkfirst=True)
+ engine.execute(
+ data_table.insert(), {"name": "row1", "data": data_element}
+ )
+
+ row = engine.execute(select([data_table.c.data])).first()
+
+ eq_(row, (data_element,))
+ eq_(js.mock_calls, [mock.call(data_element)])
+ eq_(jd.mock_calls, [mock.call(json.dumps(data_element))])
+
def test_round_trip_none_as_sql_null(self):
col = self.tables.data_table.c["nulldata"]
"""SQLite-specific tests."""
import datetime
+import json
import os
from sqlalchemy import and_
conn.scalar(select([sqlite_json.c.foo["json"]])), value["json"]
)
+ @testing.provide_metadata
+ def test_deprecated_serializer_args(self):
+ sqlite_json = Table(
+ "json_test", self.metadata, Column("foo", sqlite.JSON)
+ )
+ data_element = {"foo": "bar"}
+
+ js = mock.Mock(side_effect=json.dumps)
+ jd = mock.Mock(side_effect=json.loads)
+
+ with testing.expect_deprecated(
+ "The _json_deserializer argument to the SQLite "
+ "dialect has been renamed",
+ "The _json_serializer argument to the SQLite "
+ "dialect has been renamed",
+ ):
+ engine = engines.testing_engine(
+ options=dict(_json_serializer=js, _json_deserializer=jd)
+ )
+ self.metadata.create_all(engine)
+
+ engine.execute(sqlite_json.insert(), {"foo": data_element})
+
+ row = engine.execute(select([sqlite_json.c.foo])).first()
+
+ eq_(row, (data_element,))
+ eq_(js.mock_calls, [mock.call(data_element)])
+ eq_(jd.mock_calls, [mock.call(json.dumps(data_element))])
+
class DateTimeTest(fixtures.TestBase, AssertsCompiledSQL):
def test_time_microseconds(self):