mirror of
https://github.com/django/django.git
synced 2025-10-24 06:06:09 +00:00
Fixed #35677 -- Avoided non-sticky filtering of prefetched many-to-many.
The original queryset._next_is_sticky() call never had the intended effect as no further filtering was applied internally after the pk__in lookup making it a noop. In order to be coherent with how related filters are applied when retrieving objects from a related manager the effects of what calling _next_is_sticky() prior to applying annotations and filters to the queryset provided for prefetching are emulated by allowing the reuse of all pre-existing JOINs. Thanks David Glenck and Thiago Bellini Ribeiro for the detailed reports and tests.
This commit is contained in:
committed by
Sarah Boyce
parent
db7b1ae9f6
commit
2598b371a9
@@ -3,7 +3,7 @@ from unittest import mock
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import NotSupportedError, connection
|
||||
from django.db.models import Prefetch, QuerySet, prefetch_related_objects
|
||||
from django.db.models import F, Prefetch, QuerySet, prefetch_related_objects
|
||||
from django.db.models.query import get_prefetcher
|
||||
from django.db.models.sql import Query
|
||||
from django.test import (
|
||||
@@ -361,7 +361,7 @@ class PrefetchRelatedTests(TestDataMixin, TestCase):
|
||||
Query,
|
||||
"add_q",
|
||||
autospec=True,
|
||||
side_effect=lambda self, q: add_q(self, q),
|
||||
side_effect=lambda self, q, reuse_all: add_q(self, q),
|
||||
) as add_q_mock:
|
||||
list(Book.objects.prefetch_related(relation))
|
||||
self.assertEqual(add_q_mock.call_count, 1)
|
||||
@@ -392,6 +392,46 @@ class PrefetchRelatedTests(TestDataMixin, TestCase):
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
Book.objects.prefetch_related("authors").iterator()
|
||||
|
||||
def test_m2m_join_reuse(self):
|
||||
FavoriteAuthors.objects.bulk_create(
|
||||
[
|
||||
FavoriteAuthors(
|
||||
author=self.author1, likes_author=self.author3, is_active=True
|
||||
),
|
||||
FavoriteAuthors(
|
||||
author=self.author1,
|
||||
likes_author=self.author4,
|
||||
is_active=False,
|
||||
),
|
||||
FavoriteAuthors(
|
||||
author=self.author2, likes_author=self.author3, is_active=True
|
||||
),
|
||||
FavoriteAuthors(
|
||||
author=self.author2, likes_author=self.author4, is_active=True
|
||||
),
|
||||
]
|
||||
)
|
||||
with self.assertNumQueries(2):
|
||||
authors = list(
|
||||
Author.objects.filter(
|
||||
pk__in=[self.author1.pk, self.author2.pk]
|
||||
).prefetch_related(
|
||||
Prefetch(
|
||||
"favorite_authors",
|
||||
queryset=(
|
||||
Author.objects.annotate(
|
||||
active_favorite=F("likes_me__is_active"),
|
||||
).filter(active_favorite=True)
|
||||
),
|
||||
to_attr="active_favorite_authors",
|
||||
)
|
||||
)
|
||||
)
|
||||
self.assertEqual(authors[0].active_favorite_authors, [self.author3])
|
||||
self.assertEqual(
|
||||
authors[1].active_favorite_authors, [self.author3, self.author4]
|
||||
)
|
||||
|
||||
|
||||
class RawQuerySetTests(TestDataMixin, TestCase):
|
||||
def test_basic(self):
|
||||
@@ -1046,7 +1086,7 @@ class CustomPrefetchTests(TestCase):
|
||||
Query,
|
||||
"add_q",
|
||||
autospec=True,
|
||||
side_effect=lambda self, q: add_q(self, q),
|
||||
side_effect=lambda self, q, reuse_all: add_q(self, q),
|
||||
) as add_q_mock:
|
||||
list(
|
||||
House.objects.prefetch_related(
|
||||
|
||||
Reference in New Issue
Block a user