mirror of https://github.com/django/django.git
Fixed #35309 -- Made prefetch clear ordering for single-valued relationships.
This commit is contained in:
parent
921670c694
commit
f2388a4b73
|
@ -195,6 +195,9 @@ class ForwardManyToOneDescriptor:
|
||||||
else:
|
else:
|
||||||
query = {"%s__in" % self.field.related_query_name(): instances}
|
query = {"%s__in" % self.field.related_query_name(): instances}
|
||||||
queryset = queryset.filter(**query)
|
queryset = queryset.filter(**query)
|
||||||
|
# There can be only one object prefetched for each instance so clear
|
||||||
|
# ordering if the query allows it without side effects.
|
||||||
|
queryset.query.clear_ordering()
|
||||||
|
|
||||||
# Since we're going to assign directly in the cache,
|
# Since we're going to assign directly in the cache,
|
||||||
# we must manage the reverse relation cache manually.
|
# we must manage the reverse relation cache manually.
|
||||||
|
@ -469,6 +472,9 @@ class ReverseOneToOneDescriptor:
|
||||||
instances_dict = {instance_attr(inst): inst for inst in instances}
|
instances_dict = {instance_attr(inst): inst for inst in instances}
|
||||||
query = {"%s__in" % self.related.field.name: instances}
|
query = {"%s__in" % self.related.field.name: instances}
|
||||||
queryset = queryset.filter(**query)
|
queryset = queryset.filter(**query)
|
||||||
|
# There can be only one object prefetched for each instance so clear
|
||||||
|
# ordering if the query allows it without side effects.
|
||||||
|
queryset.query.clear_ordering()
|
||||||
|
|
||||||
# Since we're going to assign directly in the cache,
|
# Since we're going to assign directly in the cache,
|
||||||
# we must manage the reverse relation cache manually.
|
# we must manage the reverse relation cache manually.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.db.models import Prefetch, prefetch_related_objects
|
from django.db.models import Prefetch, prefetch_related_objects
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from .models import Author, Book, Reader
|
from .models import Author, Book, House, Reader, Room
|
||||||
|
|
||||||
|
|
||||||
class PrefetchRelatedObjectsTests(TestCase):
|
class PrefetchRelatedObjectsTests(TestCase):
|
||||||
|
@ -33,6 +33,17 @@ class PrefetchRelatedObjectsTests(TestCase):
|
||||||
cls.reader1.books_read.add(cls.book1, cls.book4)
|
cls.reader1.books_read.add(cls.book1, cls.book4)
|
||||||
cls.reader2.books_read.add(cls.book2, cls.book4)
|
cls.reader2.books_read.add(cls.book2, cls.book4)
|
||||||
|
|
||||||
|
cls.house1 = House.objects.create(name="b1", address="1")
|
||||||
|
cls.house2 = House.objects.create(name="b2", address="2")
|
||||||
|
|
||||||
|
cls.room1 = Room.objects.create(name="a1", house=cls.house1)
|
||||||
|
cls.room2 = Room.objects.create(name="a2", house=cls.house2)
|
||||||
|
|
||||||
|
cls.house1.main_room = cls.room1
|
||||||
|
cls.house1.save()
|
||||||
|
cls.house2.main_room = cls.room2
|
||||||
|
cls.house2.save()
|
||||||
|
|
||||||
def test_unknown(self):
|
def test_unknown(self):
|
||||||
book1 = Book.objects.get(id=self.book1.id)
|
book1 = Book.objects.get(id=self.book1.id)
|
||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(AttributeError):
|
||||||
|
@ -58,20 +69,75 @@ class PrefetchRelatedObjectsTests(TestCase):
|
||||||
|
|
||||||
def test_foreignkey_forward(self):
|
def test_foreignkey_forward(self):
|
||||||
authors = list(Author.objects.all())
|
authors = list(Author.objects.all())
|
||||||
with self.assertNumQueries(1):
|
with self.assertNumQueries(1) as ctx:
|
||||||
prefetch_related_objects(authors, "first_book")
|
prefetch_related_objects(authors, "first_book")
|
||||||
|
self.assertNotIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
with self.assertNumQueries(0):
|
with self.assertNumQueries(0):
|
||||||
[author.first_book for author in authors]
|
[author.first_book for author in authors]
|
||||||
|
|
||||||
|
authors = list(Author.objects.all())
|
||||||
|
with self.assertNumQueries(1) as ctx:
|
||||||
|
prefetch_related_objects(
|
||||||
|
authors,
|
||||||
|
Prefetch("first_book", queryset=Book.objects.order_by("-title")),
|
||||||
|
)
|
||||||
|
self.assertNotIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
def test_foreignkey_reverse(self):
|
def test_foreignkey_reverse(self):
|
||||||
books = list(Book.objects.all())
|
books = list(Book.objects.all())
|
||||||
with self.assertNumQueries(1):
|
with self.assertNumQueries(1) as ctx:
|
||||||
prefetch_related_objects(books, "first_time_authors")
|
prefetch_related_objects(books, "first_time_authors")
|
||||||
|
self.assertIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
with self.assertNumQueries(0):
|
with self.assertNumQueries(0):
|
||||||
[list(book.first_time_authors.all()) for book in books]
|
[list(book.first_time_authors.all()) for book in books]
|
||||||
|
|
||||||
|
books = list(Book.objects.all())
|
||||||
|
with self.assertNumQueries(1) as ctx:
|
||||||
|
prefetch_related_objects(
|
||||||
|
books,
|
||||||
|
Prefetch(
|
||||||
|
"first_time_authors",
|
||||||
|
queryset=Author.objects.order_by("-name"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.assertIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
|
def test_one_to_one_forward(self):
|
||||||
|
houses = list(House.objects.all())
|
||||||
|
with self.assertNumQueries(1) as ctx:
|
||||||
|
prefetch_related_objects(houses, "main_room")
|
||||||
|
self.assertNotIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
[house.main_room for house in houses]
|
||||||
|
|
||||||
|
houses = list(House.objects.all())
|
||||||
|
with self.assertNumQueries(1) as ctx:
|
||||||
|
prefetch_related_objects(
|
||||||
|
houses,
|
||||||
|
Prefetch("main_room", queryset=Room.objects.order_by("-name")),
|
||||||
|
)
|
||||||
|
self.assertNotIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
|
def test_one_to_one_reverse(self):
|
||||||
|
rooms = list(Room.objects.all())
|
||||||
|
with self.assertNumQueries(1) as ctx:
|
||||||
|
prefetch_related_objects(rooms, "main_room_of")
|
||||||
|
self.assertNotIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
[room.main_room_of for room in rooms]
|
||||||
|
|
||||||
|
rooms = list(Room.objects.all())
|
||||||
|
with self.assertNumQueries(1) as ctx:
|
||||||
|
prefetch_related_objects(
|
||||||
|
rooms,
|
||||||
|
Prefetch("main_room_of", queryset=House.objects.order_by("-name")),
|
||||||
|
)
|
||||||
|
self.assertNotIn("ORDER BY", ctx.captured_queries[0]["sql"])
|
||||||
|
|
||||||
def test_m2m_then_m2m(self):
|
def test_m2m_then_m2m(self):
|
||||||
"""A m2m can be followed through another m2m."""
|
"""A m2m can be followed through another m2m."""
|
||||||
authors = list(Author.objects.all())
|
authors = list(Author.objects.all())
|
||||||
|
|
Loading…
Reference in New Issue