mirror of
https://github.com/django/django.git
synced 2025-11-07 07:15:35 +00:00
[5.2.x] Fixed #36116 -- Optimized multi-column ForwardManyToOne prefetching.
Rely on ColPairs and TupleIn which support a single column to be specified
to avoid special casing ForwardManyToOne.get_prefetch_querysets().
Thanks Jacob Walls for the report.
Backport of 626d77e52a from main.
This commit is contained in:
committed by
Sarah Boyce
parent
55afe50aca
commit
9861e86547
@@ -107,6 +107,6 @@ class Friendship(models.Model):
|
||||
Person,
|
||||
from_fields=["to_friend_country_id", "to_friend_id"],
|
||||
to_fields=["person_country_id", "id"],
|
||||
related_name="to_friend",
|
||||
related_name="to_friend+",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import pickle
|
||||
from operator import attrgetter
|
||||
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db import models
|
||||
from django.db import connection, models
|
||||
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
|
||||
from django.test.utils import isolate_apps
|
||||
from django.utils import translation
|
||||
@@ -247,7 +247,7 @@ class MultiColumnFKTests(TestCase):
|
||||
normal_people = [m.person for m in Membership.objects.order_by("pk")]
|
||||
self.assertEqual(people, normal_people)
|
||||
|
||||
def test_prefetch_foreignkey_forward_works(self):
|
||||
def test_prefetch_foreignobject_forward(self):
|
||||
Membership.objects.create(
|
||||
membership_country=self.usa, person=self.bob, group=self.cia
|
||||
)
|
||||
@@ -264,7 +264,40 @@ class MultiColumnFKTests(TestCase):
|
||||
normal_people = [m.person for m in Membership.objects.order_by("pk")]
|
||||
self.assertEqual(people, normal_people)
|
||||
|
||||
def test_prefetch_foreignkey_reverse_works(self):
|
||||
def test_prefetch_foreignobject_hidden_forward(self):
|
||||
Friendship.objects.create(
|
||||
from_friend_country=self.usa,
|
||||
from_friend_id=self.bob.id,
|
||||
to_friend_country_id=self.usa.id,
|
||||
to_friend_id=self.george.id,
|
||||
)
|
||||
Friendship.objects.create(
|
||||
from_friend_country=self.usa,
|
||||
from_friend_id=self.bob.id,
|
||||
to_friend_country_id=self.soviet_union.id,
|
||||
to_friend_id=self.sam.id,
|
||||
)
|
||||
with self.assertNumQueries(2) as ctx:
|
||||
friendships = list(
|
||||
Friendship.objects.prefetch_related("to_friend").order_by("pk")
|
||||
)
|
||||
prefetch_sql = ctx[-1]["sql"]
|
||||
# Prefetch queryset should be filtered by all foreign related fields
|
||||
# to prevent extra rows from being eagerly fetched.
|
||||
prefetch_where_sql = prefetch_sql.split("WHERE")[-1]
|
||||
for to_field_name in Friendship.to_friend.field.to_fields:
|
||||
to_field = Person._meta.get_field(to_field_name)
|
||||
with self.subTest(to_field=to_field):
|
||||
self.assertIn(
|
||||
connection.ops.quote_name(to_field.column),
|
||||
prefetch_where_sql,
|
||||
)
|
||||
self.assertNotIn(" JOIN ", prefetch_sql)
|
||||
with self.assertNumQueries(0):
|
||||
self.assertEqual(friendships[0].to_friend, self.george)
|
||||
self.assertEqual(friendships[1].to_friend, self.sam)
|
||||
|
||||
def test_prefetch_foreignobject_reverse(self):
|
||||
Membership.objects.create(
|
||||
membership_country=self.usa, person=self.bob, group=self.cia
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user