Skip to content

Commit ca7b1f6

Browse files
committed
Optimizations play nicely with select_related, prefetch_related
1 parent 720a37d commit ca7b1f6

File tree

4 files changed

+33
-12
lines changed

4 files changed

+33
-12
lines changed

rest_framework/relations.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,7 @@ def get_attribute(self, instance):
101101

102102
def get_iterable(self, instance, source_attrs):
103103
relationship = get_attribute(instance, source_attrs)
104-
relationship = relationship.all() if (hasattr(relationship, 'all')) else relationship
105-
106-
if self.use_pk_only_optimization():
107-
# Optimized case, return mock objects only containing the pk attribute.
108-
return [
109-
PKOnlyObject(pk=pk)
110-
for pk in relationship.values_list('pk', flat=True)
111-
]
112-
113-
# Standard case, return the object instances.
114-
return relationship
104+
return relationship.all() if (hasattr(relationship, 'all')) else relationship
115105

116106
@property
117107
def choices(self):

tests/test_relations_hyperlink.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ def test_many_to_many_retrieve(self):
9292
with self.assertNumQueries(4):
9393
self.assertEqual(serializer.data, expected)
9494

95+
def test_many_to_many_retrieve_prefetch_related(self):
96+
queryset = ManyToManySource.objects.all().prefetch_related('targets')
97+
serializer = ManyToManySourceSerializer(queryset, many=True, context={'request': request})
98+
with self.assertNumQueries(2):
99+
serializer.data
100+
95101
def test_reverse_many_to_many_retrieve(self):
96102
queryset = ManyToManyTarget.objects.all()
97103
serializer = ManyToManyTargetSerializer(queryset, many=True, context={'request': request})

tests/test_relations_pk.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ def test_many_to_many_retrieve(self):
7171
with self.assertNumQueries(4):
7272
self.assertEqual(serializer.data, expected)
7373

74+
def test_many_to_many_retrieve_prefetch_related(self):
75+
queryset = ManyToManySource.objects.all().prefetch_related('targets')
76+
serializer = ManyToManySourceSerializer(queryset, many=True)
77+
with self.assertNumQueries(2):
78+
serializer.data
79+
7480
def test_reverse_many_to_many_retrieve(self):
7581
queryset = ManyToManyTarget.objects.all()
7682
serializer = ManyToManyTargetSerializer(queryset, many=True)
@@ -188,6 +194,12 @@ def test_reverse_foreign_key_retrieve(self):
188194
with self.assertNumQueries(3):
189195
self.assertEqual(serializer.data, expected)
190196

197+
def test_reverse_foreign_key_retrieve_prefetch_related(self):
198+
queryset = ForeignKeyTarget.objects.all().prefetch_related('sources')
199+
serializer = ForeignKeyTargetSerializer(queryset, many=True)
200+
with self.assertNumQueries(2):
201+
serializer.data
202+
191203
def test_foreign_key_update(self):
192204
data = {'id': 1, 'name': 'source-1', 'target': 2}
193205
instance = ForeignKeySource.objects.get(pk=1)

tests/test_relations_slug.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ def test_foreign_key_retrieve(self):
5454
{'id': 2, 'name': 'source-2', 'target': 'target-1'},
5555
{'id': 3, 'name': 'source-3', 'target': 'target-1'}
5656
]
57-
self.assertEqual(serializer.data, expected)
57+
with self.assertNumQueries(4):
58+
self.assertEqual(serializer.data, expected)
59+
60+
def test_foreign_key_retrieve_select_related(self):
61+
queryset = ForeignKeySource.objects.all().select_related('target')
62+
serializer = ForeignKeySourceSerializer(queryset, many=True)
63+
with self.assertNumQueries(1):
64+
serializer.data
5865

5966
def test_reverse_foreign_key_retrieve(self):
6067
queryset = ForeignKeyTarget.objects.all()
@@ -65,6 +72,12 @@ def test_reverse_foreign_key_retrieve(self):
6572
]
6673
self.assertEqual(serializer.data, expected)
6774

75+
def test_reverse_foreign_key_retrieve_prefetch_related(self):
76+
queryset = ForeignKeyTarget.objects.all().prefetch_related('sources')
77+
serializer = ForeignKeyTargetSerializer(queryset, many=True)
78+
with self.assertNumQueries(2):
79+
serializer.data
80+
6881
def test_foreign_key_update(self):
6982
data = {'id': 1, 'name': 'source-1', 'target': 'target-2'}
7083
instance = ForeignKeySource.objects.get(pk=1)

0 commit comments

Comments
 (0)