diff options
| author | can <cansarigol@derinbilgi.com.tr> | 2019-04-17 09:24:28 +0300 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2019-04-18 07:37:48 +0200 |
| commit | e85317d73113382c96e87f36f0d732e3084df145 (patch) | |
| tree | c12455598881edf8a29bd072c8691aefe41eeea9 | |
| parent | ef38777ee8e74fae42bceff25e74cfe776042045 (diff) | |
[2.2.x] Fixed #30335, #29139 -- Fixed crash when ordering or aggregating over a nested JSONField key transform.
Backport of d87bd29c4f8dfcdf3f4a4eb8340e6770a2416fe3 from master.
| -rw-r--r-- | django/db/models/sql/compiler.py | 11 | ||||
| -rw-r--r-- | docs/releases/2.2.1.txt | 5 | ||||
| -rw-r--r-- | tests/postgres_tests/test_json.py | 25 |
3 files changed, 36 insertions, 5 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 3e04a801e9..bfdf26d4db 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -17,6 +17,7 @@ from django.db.utils import DatabaseError, NotSupportedError from django.utils.deprecation import ( RemovedInDjango30Warning, RemovedInDjango31Warning, ) +from django.utils.hashable import make_hashable from django.utils.inspect import func_supports_parameter FORCE = object() @@ -135,9 +136,10 @@ class SQLCompiler: # wrapping () because they could be removed when a subquery is # the "rhs" in an expression (see Subquery._prepare()). sql = '(%s)' % sql - if (sql, tuple(params)) not in seen: + params_hash = make_hashable(params) + if (sql, params_hash) not in seen: result.append((sql, params)) - seen.add((sql, tuple(params))) + seen.add((sql, params_hash)) return result def collapse_group_by(self, expressions, having): @@ -361,9 +363,10 @@ class SQLCompiler: # is refactored into expressions, then we can check each part as we # generate it. 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 - seen.add((without_ordering, tuple(params))) + seen.add((without_ordering, params_hash)) result.append((resolved, (sql, params, is_ref))) return result diff --git a/docs/releases/2.2.1.txt b/docs/releases/2.2.1.txt index 3a2453dbd1..a740f516ec 100644 --- a/docs/releases/2.2.1.txt +++ b/docs/releases/2.2.1.txt @@ -33,3 +33,8 @@ Bugfixes * Reverted an optimization in Django 2.2 (:ticket:`29725`) that caused the inconsistent behavior of ``count()`` and ``exists()`` on a reverse 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`). diff --git a/tests/postgres_tests/test_json.py b/tests/postgres_tests/test_json.py index 0f82bda3c4..9acd7c8bf8 100644 --- a/tests/postgres_tests/test_json.py +++ b/tests/postgres_tests/test_json.py @@ -1,10 +1,11 @@ import datetime +import operator import uuid from decimal import Decimal from django.core import checks, exceptions, serializers 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.test.utils import isolate_apps from django.utils.html import escape @@ -15,6 +16,7 @@ from .models import JSONModel, PostgreSQLModel try: from django.contrib.postgres import forms from django.contrib.postgres.fields import JSONField + from django.contrib.postgres.fields.jsonb import KeyTextTransform, KeyTransform except ImportError: pass @@ -153,6 +155,27 @@ class TestQuerying(PostgreSQLTestCase): 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]]) + 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): query = JSONModel.objects.values_list('field__k__l') self.assertSequenceEqual( |
