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:
committed by
Sarah Boyce
parent
5488530a27
commit
e709301000
@@ -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(), []
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user