summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2017-04-27 00:49:17 -0400
committerSimon Charette <charette.s@gmail.com>2017-05-11 21:50:07 -0400
commit4acae21846f6212aa992763e587c7e201828d7b0 (patch)
treebb2526ebca5fe788b71dd7c78013124c623866df
parenta9874d48b1b9d91988b9f299726ec4f559fb2f75 (diff)
Fixed #24254 -- Fixed queries using the __in lookup with querysets using distinct() and order_by().
Thanks Tim for the review.
-rw-r--r--django/db/models/sql/compiler.py25
-rw-r--r--tests/queries/tests.py30
2 files changed, 51 insertions, 4 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 2543755952..6874ae6ef4 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -523,6 +523,31 @@ class SQLCompiler:
if for_update_part and not self.connection.features.for_update_after_from:
result.append(for_update_part)
+ if self.query.subquery and extra_select:
+ # If the query is used as a subquery, the extra selects would
+ # result in more columns than the left-hand side expression is
+ # expecting. This can happen when a subquery uses a combination
+ # of order_by() and distinct(), forcing the ordering expressions
+ # to be selected as well. Wrap the query in another subquery
+ # to exclude extraneous selects.
+ sub_selects = []
+ sub_params = []
+ for select, _, alias in self.select:
+ if alias:
+ sub_selects.append("%s.%s" % (
+ self.connection.ops.quote_name('subquery'),
+ self.connection.ops.quote_name(alias),
+ ))
+ else:
+ select_clone = select.relabeled_clone({select.alias: 'subquery'})
+ subselect, subparams = select_clone.as_sql(self, self.connection)
+ sub_selects.append(subselect)
+ sub_params.extend(subparams)
+ return 'SELECT %s FROM (%s) AS subquery' % (
+ ', '.join(sub_selects),
+ ' '.join(result),
+ ), sub_params + params
+
return ' '.join(result), tuple(params)
finally:
# Finally do cleanup - get rid of the joins we created above.
diff --git a/tests/queries/tests.py b/tests/queries/tests.py
index 8bcf38f146..d1c1b2b4b0 100644
--- a/tests/queries/tests.py
+++ b/tests/queries/tests.py
@@ -2005,10 +2005,10 @@ class QuerysetOrderedTests(unittest.TestCase):
class SubqueryTests(TestCase):
@classmethod
def setUpTestData(cls):
- DumbCategory.objects.create(id=1)
- DumbCategory.objects.create(id=2)
- DumbCategory.objects.create(id=3)
- DumbCategory.objects.create(id=4)
+ NamedCategory.objects.create(id=1, name='first')
+ NamedCategory.objects.create(id=2, name='second')
+ NamedCategory.objects.create(id=3, name='third')
+ NamedCategory.objects.create(id=4, name='fourth')
def test_ordered_subselect(self):
"Subselects honor any manual ordering"
@@ -2064,6 +2064,28 @@ class SubqueryTests(TestCase):
DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[1:]).delete()
self.assertEqual(set(DumbCategory.objects.values_list('id', flat=True)), {3})
+ def test_distinct_ordered_sliced_subquery(self):
+ # Implicit values('id').
+ self.assertSequenceEqual(
+ NamedCategory.objects.filter(
+ id__in=NamedCategory.objects.distinct().order_by('name')[0:2],
+ ).order_by('name').values_list('name', flat=True), ['first', 'fourth']
+ )
+ # Explicit values('id').
+ self.assertSequenceEqual(
+ NamedCategory.objects.filter(
+ id__in=NamedCategory.objects.distinct().order_by('-name').values('id')[0:2],
+ ).order_by('name').values_list('name', flat=True), ['second', 'third']
+ )
+ # Annotated value.
+ self.assertSequenceEqual(
+ DumbCategory.objects.filter(
+ id__in=DumbCategory.objects.annotate(
+ double_id=F('id') * 2
+ ).order_by('id').distinct().values('double_id')[0:2],
+ ).order_by('id').values_list('id', flat=True), [2, 4]
+ )
+
class CloneTests(TestCase):