Fixed #30335, #29139 -- Fixed crash when ordering or aggregating over a nested JSONField key transform.
This commit is contained in:
parent
f110de5c04
commit
d87bd29c4f
|
@ -14,6 +14,7 @@ from django.db.models.sql.query import Query, get_order_dir
|
||||||
from django.db.transaction import TransactionManagementError
|
from django.db.transaction import TransactionManagementError
|
||||||
from django.db.utils import DatabaseError, NotSupportedError
|
from django.db.utils import DatabaseError, NotSupportedError
|
||||||
from django.utils.deprecation import RemovedInDjango31Warning
|
from django.utils.deprecation import RemovedInDjango31Warning
|
||||||
|
from django.utils.hashable import make_hashable
|
||||||
|
|
||||||
FORCE = object()
|
FORCE = object()
|
||||||
|
|
||||||
|
@ -126,9 +127,10 @@ class SQLCompiler:
|
||||||
|
|
||||||
for expr in expressions:
|
for expr in expressions:
|
||||||
sql, params = self.compile(expr)
|
sql, params = self.compile(expr)
|
||||||
if (sql, tuple(params)) not in seen:
|
params_hash = make_hashable(params)
|
||||||
|
if (sql, params_hash) not in seen:
|
||||||
result.append((sql, params))
|
result.append((sql, params))
|
||||||
seen.add((sql, tuple(params)))
|
seen.add((sql, params_hash))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def collapse_group_by(self, expressions, having):
|
def collapse_group_by(self, expressions, having):
|
||||||
|
@ -352,9 +354,10 @@ class SQLCompiler:
|
||||||
# is refactored into expressions, then we can check each part as we
|
# is refactored into expressions, then we can check each part as we
|
||||||
# generate it.
|
# generate it.
|
||||||
without_ordering = self.ordering_parts.search(sql).group(1)
|
without_ordering = self.ordering_parts.search(sql).group(1)
|
||||||
if (without_ordering, tuple(params)) in seen:
|
params_hash = make_hashable(params)
|
||||||
|
if (without_ordering, params_hash) in seen:
|
||||||
continue
|
continue
|
||||||
seen.add((without_ordering, tuple(params)))
|
seen.add((without_ordering, params_hash))
|
||||||
result.append((resolved, (sql, params, is_ref)))
|
result.append((resolved, (sql, params, is_ref)))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -33,3 +33,8 @@ Bugfixes
|
||||||
* Reverted an optimization in Django 2.2 (:ticket:`29725`) that caused the
|
* Reverted an optimization in Django 2.2 (:ticket:`29725`) that caused the
|
||||||
inconsistent behavior of ``count()`` and ``exists()`` on a reverse
|
inconsistent behavior of ``count()`` and ``exists()`` on a reverse
|
||||||
many-to-many relationship with a custom manager (:ticket:`30325`).
|
many-to-many relationship with a custom manager (:ticket:`30325`).
|
||||||
|
|
||||||
|
* Fixed a regression in Django 2.2 where
|
||||||
|
:class:`~django.core.paginator.Paginator` crashed when ``object_list`` was
|
||||||
|
a queryset ordered or aggregated over a nested ``JSONField`` key transform
|
||||||
|
(:ticket:`30335`).
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
import operator
|
||||||
import uuid
|
import uuid
|
||||||
from decimal import Decimal
|
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.models import Q
|
from django.db.models import Count, Q
|
||||||
from django.forms import CharField, Form, widgets
|
from django.forms import CharField, Form, widgets
|
||||||
from django.test.utils import isolate_apps
|
from django.test.utils import isolate_apps
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
@ -15,6 +16,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 KeyTextTransform, KeyTransform
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -162,6 +164,27 @@ 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_ordering_grouping_by_key_transform(self):
|
||||||
|
base_qs = JSONModel.objects.filter(field__d__0__isnull=False)
|
||||||
|
for qs in (
|
||||||
|
base_qs.order_by('field__d__0'),
|
||||||
|
base_qs.annotate(key=KeyTransform('0', KeyTransform('d', 'field'))).order_by('key'),
|
||||||
|
):
|
||||||
|
self.assertSequenceEqual(qs, [self.objs[8]])
|
||||||
|
qs = JSONModel.objects.filter(field__isnull=False)
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
qs.values('field__d__0').annotate(count=Count('field__d__0')).order_by('count'),
|
||||||
|
[1, 10],
|
||||||
|
operator.itemgetter('count'),
|
||||||
|
)
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
qs.filter(field__isnull=False).annotate(
|
||||||
|
key=KeyTextTransform('f', KeyTransform('1', KeyTransform('d', 'field'))),
|
||||||
|
).values('key').annotate(count=Count('key')).order_by('count'),
|
||||||
|
[(None, 0), ('g', 1)],
|
||||||
|
operator.itemgetter('key', 'count'),
|
||||||
|
)
|
||||||
|
|
||||||
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(
|
||||||
|
|
Loading…
Reference in New Issue