mirror of
https://github.com/django/django.git
synced 2025-06-03 18:49:12 +00:00
[5.2.x] Fixed #36290 -- Made TupleIn() lookup discard tuples containing None.
Just like the In() lookup discards of None members TupleIn() should discard tuples containing any None as NULL != NULL in SQL and the framework expects such queries to be elided under some circumstances. Refs #31667, #36116. Thanks Basptise Mispelon for bisecting the regression to 626d77e. Backport of f7f38f3a0b44d8c6d14344dae66b6ce52cd77b55 from main
This commit is contained in:
parent
317690403a
commit
8ebdd37a0b
@ -318,6 +318,10 @@ class TupleIn(TupleLookupMixin, In):
|
|||||||
lhs = self.lhs
|
lhs = self.lhs
|
||||||
|
|
||||||
for vals in rhs:
|
for vals in rhs:
|
||||||
|
# Remove any tuple containing None from the list as NULL is never
|
||||||
|
# equal to anything.
|
||||||
|
if any(val is None for val in vals):
|
||||||
|
continue
|
||||||
result.append(
|
result.append(
|
||||||
Tuple(
|
Tuple(
|
||||||
*[
|
*[
|
||||||
@ -327,6 +331,9 @@ class TupleIn(TupleLookupMixin, In):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
raise EmptyResultSet
|
||||||
|
|
||||||
return compiler.compile(Tuple(*result))
|
return compiler.compile(Tuple(*result))
|
||||||
|
|
||||||
def get_fallback_sql(self, compiler, connection):
|
def get_fallback_sql(self, compiler, connection):
|
||||||
@ -342,9 +349,15 @@ class TupleIn(TupleLookupMixin, In):
|
|||||||
lhs = self.lhs
|
lhs = self.lhs
|
||||||
|
|
||||||
for vals in rhs:
|
for vals in rhs:
|
||||||
|
# Remove any tuple containing None from the list as NULL is never
|
||||||
|
# equal to anything.
|
||||||
|
if any(val is None for val in vals):
|
||||||
|
continue
|
||||||
lookups = [Exact(col, val) for col, val in zip(lhs, vals)]
|
lookups = [Exact(col, val) for col, val in zip(lhs, vals)]
|
||||||
root.children.append(WhereNode(lookups, connector=AND))
|
root.children.append(WhereNode(lookups, connector=AND))
|
||||||
|
|
||||||
|
if not root.children:
|
||||||
|
raise EmptyResultSet
|
||||||
return root.as_sql(compiler, connection)
|
return root.as_sql(compiler, connection)
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,3 +12,6 @@ Bugfixes
|
|||||||
* Fixed a regression in Django 5.2 that caused a crash when annotating
|
* Fixed a regression in Django 5.2 that caused a crash when annotating
|
||||||
aggregate expressions over query that uses explicit grouping by transforms
|
aggregate expressions over query that uses explicit grouping by transforms
|
||||||
followed by field references (:ticket:`36292`).
|
followed by field references (:ticket:`36292`).
|
||||||
|
|
||||||
|
* Fixed a regression in Django 5.2 that caused unnecessary queries when
|
||||||
|
prefetching nullable foreign key relationships (:ticket:`36290`).
|
||||||
|
@ -206,6 +206,13 @@ class CompositePKFilterTests(TestCase):
|
|||||||
[self.comment_1],
|
[self.comment_1],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_filter_by_pk_in_none(self):
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
self.assertSequenceEqual(
|
||||||
|
Comment.objects.filter(pk__in=[(None, 1), (1, None)]),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
def test_filter_comments_by_user_and_order_by_pk_asc(self):
|
def test_filter_comments_by_user_and_order_by_pk_asc(self):
|
||||||
self.assertSequenceEqual(
|
self.assertSequenceEqual(
|
||||||
Comment.objects.filter(user=self.user_1).order_by("pk"),
|
Comment.objects.filter(user=self.user_1).order_by("pk"),
|
||||||
|
@ -84,7 +84,7 @@ class Friendship(models.Model):
|
|||||||
)
|
)
|
||||||
from_friend_id = models.IntegerField()
|
from_friend_id = models.IntegerField()
|
||||||
to_friend_country_id = models.IntegerField()
|
to_friend_country_id = models.IntegerField()
|
||||||
to_friend_id = models.IntegerField()
|
to_friend_id = models.IntegerField(null=True)
|
||||||
|
|
||||||
# Relation Fields
|
# Relation Fields
|
||||||
from_friend = models.ForeignObject(
|
from_friend = models.ForeignObject(
|
||||||
|
@ -297,6 +297,19 @@ class MultiColumnFKTests(TestCase):
|
|||||||
self.assertEqual(friendships[0].to_friend, self.george)
|
self.assertEqual(friendships[0].to_friend, self.george)
|
||||||
self.assertEqual(friendships[1].to_friend, self.sam)
|
self.assertEqual(friendships[1].to_friend, self.sam)
|
||||||
|
|
||||||
|
def test_prefetch_foreignobject_null_hidden_forward_skipped(self):
|
||||||
|
fiendship = Friendship.objects.create(
|
||||||
|
from_friend_country=self.usa,
|
||||||
|
from_friend_id=self.bob.id,
|
||||||
|
to_friend_country_id=self.usa.id,
|
||||||
|
to_friend_id=None,
|
||||||
|
)
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
self.assertEqual(
|
||||||
|
Friendship.objects.prefetch_related("to_friend").get(),
|
||||||
|
fiendship,
|
||||||
|
)
|
||||||
|
|
||||||
def test_prefetch_foreignobject_reverse(self):
|
def test_prefetch_foreignobject_reverse(self):
|
||||||
Membership.objects.create(
|
Membership.objects.create(
|
||||||
membership_country=self.usa, person=self.bob, group=self.cia
|
membership_country=self.usa, person=self.bob, group=self.cia
|
||||||
|
Loading…
x
Reference in New Issue
Block a user