Skip to content

Commit

Permalink
Fixed #28497 -- Restored the ability to use sliced QuerySets with __e…
Browse files Browse the repository at this point in the history
…xact.

Regression in ec50937.

Thanks Simon Charette for review.
  • Loading branch information
timgraham committed Oct 16, 2017
1 parent 61a6245 commit 1b73ccc
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 0 deletions.
14 changes: 14 additions & 0 deletions django/db/models/lookups.py
Expand Up @@ -245,6 +245,20 @@ def batch_process_rhs(self, compiler, connection, rhs=None):
class Exact(FieldGetDbPrepValueMixin, BuiltinLookup):
lookup_name = 'exact'

def process_rhs(self, compiler, connection):
from django.db.models.sql.query import Query
if isinstance(self.rhs, Query):
if self.rhs.has_limit_one():
# The subquery must select only the pk.
self.rhs.clear_select_clause()
self.rhs.add_fields(['pk'])
else:
raise ValueError(
'The QuerySet value for an exact lookup must be limited to '
'one result using slicing.'
)
return super().process_rhs(compiler, connection)


@Field.register_lookup
class IExact(BuiltinLookup):
Expand Down
3 changes: 3 additions & 0 deletions django/db/models/sql/query.py
Expand Up @@ -1627,6 +1627,9 @@ def clear_limits(self):
"""Clear any existing limits."""
self.low_mark, self.high_mark = 0, None

def has_limit_one(self):
return self.high_mark is not None and (self.high_mark - self.low_mark) == 1

def can_filter(self):
"""
Return True if adding filters to this instance is still possible.
Expand Down
22 changes: 22 additions & 0 deletions tests/lookup/tests.py
Expand Up @@ -848,6 +848,28 @@ def test_exact_none_transform(self):
self.assertTrue(Season.objects.filter(nulled_text_field__nulled__exact=None))
self.assertTrue(Season.objects.filter(nulled_text_field__nulled=None))

def test_exact_sliced_queryset_limit_one(self):
self.assertCountEqual(
Article.objects.filter(author=Author.objects.all()[:1]),
[self.a1, self.a2, self.a3, self.a4]
)

def test_exact_sliced_queryset_limit_one_offset(self):
self.assertCountEqual(
Article.objects.filter(author=Author.objects.all()[1:2]),
[self.a5, self.a6, self.a7]
)

def test_exact_sliced_queryset_not_limited_to_one(self):
msg = (
'The QuerySet value for an exact lookup must be limited to one '
'result using slicing.'
)
with self.assertRaisesMessage(ValueError, msg):
list(Article.objects.filter(author=Author.objects.all()[:2]))
with self.assertRaisesMessage(ValueError, msg):
list(Article.objects.filter(author=Author.objects.all()[1:]))

def test_custom_field_none_rhs(self):
"""
__exact=value is transformed to __isnull=True if Field.get_prep_value()
Expand Down

0 comments on commit 1b73ccc

Please sign in to comment.