1
0
mirror of https://github.com/django/django.git synced 2025-10-24 14:16:09 +00:00

Fixed #36282 -- Used prefetched values in ForwardManyToOneDescriptor from indirect ancestors.

When looking for cached values in ManyRelatedManager and
ForwardManyToOneDescriptor walk up the whole chain of ancestors
(as long as they are cached) to find the prefetched relation.
This commit is contained in:
Take Weiland
2025-03-30 11:53:32 +02:00
committed by Sarah Boyce
parent 5488530a27
commit e709301000
3 changed files with 225 additions and 22 deletions

View File

@@ -21,6 +21,7 @@ from .models import (
Author2,
AuthorAddress,
AuthorWithAge,
AuthorWithAgeChild,
Bio,
Book,
Bookmark,
@@ -2086,3 +2087,184 @@ class PrefetchLimitTests(TestDataMixin, TestCase):
with self.subTest(book=book):
self.assertEqual(len(book.authors_sliced), 1)
self.assertIn(book.authors_sliced[0], list(book.authors.all()))
class PrefetchRelatedMTICacheTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.book = Book.objects.create(title="Book1")
cls.related1 = Author.objects.create(name="related1", first_book=cls.book)
cls.related2 = Author.objects.create(name="related2", first_book=cls.book)
cls.related3 = Author.objects.create(name="related3", first_book=cls.book)
cls.related4 = Author.objects.create(name="related4", first_book=cls.book)
cls.child = AuthorWithAgeChild.objects.create(
name="child",
age=31,
first_book=cls.book,
)
cls.m2m_child = AuthorWithAgeChild.objects.create(
name="m2m_child",
age=31,
first_book=cls.book,
)
cls.m2m_child.favorite_authors.set([cls.related1, cls.related2, cls.related3])
def test_parent_fk_available_in_child(self):
qs = (
Author.objects.select_related("authorwithage")
.prefetch_related("first_book")
.filter(pk=self.child.pk)
)
with self.assertNumQueries(2):
results = list(qs)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].authorwithage.first_book, self.book)
def test_grandparent_fk_available_in_child(self):
qs = (
Author.objects.select_related(
"authorwithage", "authorwithage__authorwithagechild"
)
.prefetch_related("first_book")
.filter(pk=self.child.pk)
)
with self.assertNumQueries(2):
results = list(qs)
self.assertEqual(len(results), 1)
self.assertEqual(
results[0].authorwithage.authorwithagechild.first_book, self.book
)
def test_parent_m2m_available_in_child(self):
qs = (
Author.objects.select_related("authorwithage")
.prefetch_related("favorite_authors")
.filter(pk=self.m2m_child.pk)
)
with self.assertNumQueries(2):
results = list(qs)
self.assertEqual(len(results), 1)
self.assertQuerySetEqual(
results[0].authorwithage.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
def test_grandparent_m2m_available_in_child(self):
qs = (
Author.objects.select_related(
"authorwithage", "authorwithage__authorwithagechild"
)
.prefetch_related("favorite_authors")
.filter(pk=self.m2m_child.pk)
)
with self.assertNumQueries(2):
results = list(qs)
self.assertEqual(len(results), 1)
self.assertQuerySetEqual(
set(results[0].authorwithage.authorwithagechild.favorite_authors.all()),
{self.related1, self.related2, self.related3},
)
def test_add_clears_prefetched_objects_in_parent(self):
gp = (
Author.objects.select_related("authorwithage")
.prefetch_related("favorite_authors")
.get(pk=self.m2m_child.pk)
)
self.assertQuerySetEqual(
gp.favorite_authors.all(),
{self.related1, self.related2, self.related3},
)
self.assertQuerySetEqual(
gp.authorwithage.favorite_authors.all(),
{self.related1, self.related2, self.related3},
)
gp.authorwithage.favorite_authors.add(self.related4)
self.assertQuerySetEqual(
gp.favorite_authors.all(),
{self.related1, self.related2, self.related3, self.related4},
)
self.assertQuerySetEqual(
gp.authorwithage.favorite_authors.all(),
{self.related1, self.related2, self.related3, self.related4},
)
def test_add_clears_prefetched_objects_in_grandparent(self):
gp = (
Author.objects.select_related(
"authorwithage", "authorwithage__authorwithagechild"
)
.prefetch_related("favorite_authors")
.get(pk=self.m2m_child.pk)
)
self.assertQuerySetEqual(
gp.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
self.assertQuerySetEqual(
gp.authorwithage.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
self.assertQuerySetEqual(
gp.authorwithage.authorwithagechild.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
gp.authorwithage.authorwithagechild.favorite_authors.add(self.related4)
self.assertQuerySetEqual(
gp.favorite_authors.all(),
[self.related1, self.related2, self.related3, self.related4],
)
self.assertQuerySetEqual(
gp.authorwithage.favorite_authors.all(),
[self.related1, self.related2, self.related3, self.related4],
)
self.assertQuerySetEqual(
gp.authorwithage.authorwithagechild.favorite_authors.all(),
[self.related1, self.related2, self.related3, self.related4],
)
def test_remove_clears_prefetched_objects_in_parent(self):
gp = (
Author.objects.select_related("authorwithage")
.prefetch_related("favorite_authors")
.get(pk=self.m2m_child.pk)
)
self.assertQuerySetEqual(
gp.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
self.assertQuerySetEqual(
gp.authorwithage.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
gp.authorwithage.favorite_authors.clear()
self.assertQuerySetEqual(gp.favorite_authors.all(), [])
self.assertQuerySetEqual(gp.authorwithage.favorite_authors.all(), [])
def test_remove_clears_prefetched_objects_in_grandparent(self):
gp = (
Author.objects.select_related(
"authorwithage", "authorwithage__authorwithagechild"
)
.prefetch_related("favorite_authors")
.get(pk=self.m2m_child.pk)
)
self.assertQuerySetEqual(
gp.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
self.assertQuerySetEqual(
gp.authorwithage.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
self.assertQuerySetEqual(
gp.authorwithage.authorwithagechild.favorite_authors.all(),
[self.related1, self.related2, self.related3],
)
gp.authorwithage.favorite_authors.clear()
self.assertQuerySetEqual(gp.favorite_authors.all(), [])
self.assertQuerySetEqual(gp.authorwithage.favorite_authors.all(), [])
self.assertQuerySetEqual(
gp.authorwithage.authorwithagechild.favorite_authors.all(), []
)