[2.0.x] Fixed #28497 -- Restored the ability to use sliced QuerySets with __exact.
Regression inec50937bcb
. Thanks Simon Charette for review. Backport of1b73ccc4bf
from master
This commit is contained in:
parent
04a92ebe49
commit
9b3b7804d2
|
@ -245,6 +245,20 @@ class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin):
|
||||||
class Exact(FieldGetDbPrepValueMixin, BuiltinLookup):
|
class Exact(FieldGetDbPrepValueMixin, BuiltinLookup):
|
||||||
lookup_name = 'exact'
|
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
|
@Field.register_lookup
|
||||||
class IExact(BuiltinLookup):
|
class IExact(BuiltinLookup):
|
||||||
|
|
|
@ -1627,6 +1627,9 @@ class Query:
|
||||||
"""Clear any existing limits."""
|
"""Clear any existing limits."""
|
||||||
self.low_mark, self.high_mark = 0, None
|
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):
|
def can_filter(self):
|
||||||
"""
|
"""
|
||||||
Return True if adding filters to this instance is still possible.
|
Return True if adding filters to this instance is still possible.
|
||||||
|
|
|
@ -848,6 +848,28 @@ class LookupTests(TestCase):
|
||||||
self.assertTrue(Season.objects.filter(nulled_text_field__nulled__exact=None))
|
self.assertTrue(Season.objects.filter(nulled_text_field__nulled__exact=None))
|
||||||
self.assertTrue(Season.objects.filter(nulled_text_field__nulled=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):
|
def test_custom_field_none_rhs(self):
|
||||||
"""
|
"""
|
||||||
__exact=value is transformed to __isnull=True if Field.get_prep_value()
|
__exact=value is transformed to __isnull=True if Field.get_prep_value()
|
||||||
|
|
Loading…
Reference in New Issue