[2.1.x] Fixed #30672 -- Fixed crash of JSONField/HStoreField key transforms on expressions with params.

Regression in 4f5b58f5cd.

Thanks Florian Apolloner for the report and helping with tests.

Backport of 1f8382d34d from master.
This commit is contained in:
Mariusz Felisiak 2019-08-14 15:25:35 +02:00
parent 46c2856543
commit 968b9af9b7
7 changed files with 64 additions and 3 deletions

View File

@ -86,7 +86,7 @@ class KeyTransform(Transform):
def as_sql(self, compiler, connection): def as_sql(self, compiler, connection):
lhs, params = compiler.compile(self.lhs) lhs, params = compiler.compile(self.lhs)
return '(%s -> %%s)' % lhs, [self.key_name] + params return '(%s -> %%s)' % lhs, params + [self.key_name]
class KeyTransformFactory: class KeyTransformFactory:

View File

@ -112,7 +112,7 @@ class KeyTransform(Transform):
lookup = int(self.key_name) lookup = int(self.key_name)
except ValueError: except ValueError:
lookup = self.key_name lookup = self.key_name
return '(%s %s %%s)' % (lhs, self.operator), [lookup] + params return '(%s %s %%s)' % (lhs, self.operator), params + [lookup]
class KeyTextTransform(KeyTransform): class KeyTextTransform(KeyTransform):

15
docs/releases/1.11.24.txt Normal file
View File

@ -0,0 +1,15 @@
============================
Django 1.11.24 release notes
============================
*Expected September 2, 2019*
Django 1.11.24 fixes a regression in 1.11.23.
Bugfixes
========
* Fixed crash of ``KeyTransform()`` for
:class:`~django.contrib.postgres.fields.JSONField` and
:class:`~django.contrib.postgres.fields.HStoreField` when using on
expressions with params (:ticket:`30672`).

15
docs/releases/2.1.12.txt Normal file
View File

@ -0,0 +1,15 @@
===========================
Django 2.1.12 release notes
===========================
*Expected September 2, 2019*
Django 2.1.12 fixes a regression in 2.1.11.
Bugfixes
========
* Fixed crash of ``KeyTransform()`` for
:class:`~django.contrib.postgres.fields.JSONField` and
:class:`~django.contrib.postgres.fields.HStoreField` when using on
expressions with params (:ticket:`30672`).

View File

@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
2.1.12
2.1.11 2.1.11
2.1.10 2.1.10
2.1.9 2.1.9
@ -63,6 +64,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
1.11.24
1.11.23 1.11.23
1.11.22 1.11.22
1.11.21 1.11.21

View File

@ -2,6 +2,7 @@ import json
from django.core import checks, exceptions, serializers from django.core import checks, exceptions, serializers
from django.db import connection from django.db import connection
from django.db.models.expressions import RawSQL
from django.forms import Form from django.forms import Form
from django.test.utils import ( from django.test.utils import (
CaptureQueriesContext, isolate_apps, modify_settings, CaptureQueriesContext, isolate_apps, modify_settings,
@ -13,6 +14,7 @@ from .models import HStoreModel, PostgreSQLModel
try: try:
from django.contrib.postgres import forms from django.contrib.postgres import forms
from django.contrib.postgres.fields import HStoreField from django.contrib.postgres.fields import HStoreField
from django.contrib.postgres.fields.hstore import KeyTransform
from django.contrib.postgres.validators import KeysValidator from django.contrib.postgres.validators import KeysValidator
except ImportError: except ImportError:
pass pass
@ -133,6 +135,13 @@ class TestQuerying(HStoreTestCase):
self.objs[:2] self.objs[:2]
) )
def test_key_transform_raw_expression(self):
expr = RawSQL('%s::hstore', ['x => b, y => c'])
self.assertSequenceEqual(
HStoreModel.objects.filter(field__a=KeyTransform('x', expr)),
self.objs[:2]
)
def test_keys(self): def test_keys(self):
self.assertSequenceEqual( self.assertSequenceEqual(
HStoreModel.objects.filter(field__keys=['a']), HStoreModel.objects.filter(field__keys=['a']),

View File

@ -5,7 +5,9 @@ from decimal import Decimal
from django.core import checks, exceptions, serializers from django.core import checks, exceptions, serializers
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.db import connection from django.db import connection
from django.db.models import Q from django.db.models import F, Q
from django.db.models.expressions import RawSQL
from django.db.models.functions import Cast
from django.forms import CharField, Form, widgets from django.forms import CharField, Form, widgets
from django.test.utils import CaptureQueriesContext, isolate_apps from django.test.utils import CaptureQueriesContext, isolate_apps
from django.utils.html import escape from django.utils.html import escape
@ -16,6 +18,7 @@ from .models import JSONModel, PostgreSQLModel
try: try:
from django.contrib.postgres import forms from django.contrib.postgres import forms
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from django.contrib.postgres.fields.jsonb import KeyTransform
except ImportError: except ImportError:
pass pass
@ -154,6 +157,23 @@ class TestQuerying(PostgreSQLTestCase):
query = JSONModel.objects.filter(field__name__isnull=False).order_by('field__ord') query = JSONModel.objects.filter(field__name__isnull=False).order_by('field__ord')
self.assertSequenceEqual(query, [objs[4], objs[2], objs[3], objs[1], objs[0]]) self.assertSequenceEqual(query, [objs[4], objs[2], objs[3], objs[1], objs[0]])
def test_key_transform_raw_expression(self):
expr = RawSQL('%s::jsonb', ['{"x": "bar"}'])
self.assertSequenceEqual(
JSONModel.objects.filter(field__foo=KeyTransform('x', expr)),
[self.objs[-1]],
)
def test_key_transform_expression(self):
self.assertSequenceEqual(
JSONModel.objects.filter(field__d__0__isnull=False).annotate(
key=KeyTransform('d', 'field'),
chain=KeyTransform('0', 'key'),
expr=KeyTransform('0', Cast('key', JSONField())),
).filter(chain=F('expr')),
[self.objs[8]],
)
def test_deep_values(self): def test_deep_values(self):
query = JSONModel.objects.values_list('field__k__l') query = JSONModel.objects.values_list('field__k__l')
self.assertSequenceEqual( self.assertSequenceEqual(