From fe6e5824218bab7cf47dee112d68325b338f9947 Mon Sep 17 00:00:00 2001 From: sage Date: Wed, 18 Nov 2020 11:50:06 +0700 Subject: [PATCH] Fixed #32203 -- Fixed QuerySet.values()/values_list() crash on key transforms with non-string values on SQLite. Thanks Gordon Wrigley for the report. --- django/db/models/fields/json.py | 4 ++++ docs/releases/3.1.4.txt | 4 ++++ tests/model_fields/test_jsonfield.py | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/django/db/models/fields/json.py b/django/db/models/fields/json.py index 48512ce59c5..340b7ee875a 100644 --- a/django/db/models/fields/json.py +++ b/django/db/models/fields/json.py @@ -75,6 +75,10 @@ class JSONField(CheckFieldDefaultMixin, Field): def from_db_value(self, value, expression, connection): if value is None: return value + # Some backends (SQLite at least) extract non-string values in their + # SQL datatypes. + if isinstance(expression, KeyTransform) and not isinstance(value, str): + return value try: return json.loads(value, cls=self.decoder) except json.JSONDecodeError: diff --git a/docs/releases/3.1.4.txt b/docs/releases/3.1.4.txt index 080db4f2903..74e679cb8cc 100644 --- a/docs/releases/3.1.4.txt +++ b/docs/releases/3.1.4.txt @@ -28,3 +28,7 @@ Bugfixes * Fixed a regression in Django 3.1 that caused suppressing connection errors when :class:`~django.db.models.JSONField` is used on SQLite (:ticket:`32224`). + +* Fixed a crash on SQLite, when ``QuerySet.values()/values_list()`` contained + key transforms for :class:`~django.db.models.JSONField` returning non-string + primitive values (:ticket:`32203`). diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index 23479b4c0da..c1bacbe7506 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -277,6 +277,7 @@ class TestQuerying(TestCase): 'k': {'l': 'm'}, 'n': [None], 'o': '"quoted"', + 'p': 4.2, }, [1, [2]], {'k': True, 'l': False, 'foo': 'bax'}, @@ -753,10 +754,14 @@ class TestQuerying(TestCase): qs = NullableJSONModel.objects.filter(value__h=True) tests = [ ('value__a', 'b'), + ('value__c', 14), ('value__d', ['e', {'f': 'g'}]), + ('value__h', True), + ('value__i', False), ('value__j', None), ('value__k', {'l': 'm'}), ('value__n', [None]), + ('value__p', 4.2), ] for lookup, expected in tests: with self.subTest(lookup=lookup):